diff options
105 files changed, 3371 insertions, 1747 deletions
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 77c92512a..eae4fbcb5 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml | |||
@@ -95,9 +95,6 @@ jobs: | |||
95 | - name: Copy vscode extension | 95 | - name: Copy vscode extension |
96 | run: mkdir -p ./dist/code && cp ./editors/code/*.vsix ./dist/ | 96 | run: mkdir -p ./dist/code && cp ./editors/code/*.vsix ./dist/ |
97 | 97 | ||
98 | - name: Copy emacs mode | ||
99 | run: cp ./editors/emacs/rust-analyzer.el ./dist/rust-analyzer.el | ||
100 | |||
101 | - name: Upload artifacts | 98 | - name: Upload artifacts |
102 | uses: actions/upload-artifact@v1 | 99 | uses: actions/upload-artifact@v1 |
103 | with: | 100 | with: |
@@ -109,6 +106,17 @@ jobs: | |||
109 | runs-on: ubuntu-latest | 106 | runs-on: ubuntu-latest |
110 | needs: ['build-server', 'build-clients'] | 107 | needs: ['build-server', 'build-clients'] |
111 | steps: | 108 | steps: |
109 | - name: Install Nodejs | ||
110 | uses: actions/setup-node@v1 | ||
111 | with: | ||
112 | node-version: 12.x | ||
113 | |||
114 | - run: echo "::set-env name=TAG::$(date --iso)" | ||
115 | - run: 'echo "TAG: $TAG"' | ||
116 | |||
117 | - name: Checkout repository | ||
118 | uses: actions/checkout@v1 | ||
119 | |||
112 | - uses: actions/download-artifact@v1 | 120 | - uses: actions/download-artifact@v1 |
113 | with: | 121 | with: |
114 | name: editor-plugins | 122 | name: editor-plugins |
@@ -127,9 +135,6 @@ jobs: | |||
127 | path: dist | 135 | path: dist |
128 | - run: ls -all ./dist | 136 | - run: ls -all ./dist |
129 | 137 | ||
130 | - run: echo "::set-env name=TAG::$(date --iso)" | ||
131 | - run: 'echo "TAG: $TAG"' | ||
132 | |||
133 | - name: Create Release | 138 | - name: Create Release |
134 | id: create_release | 139 | id: create_release |
135 | # uses: actions/create-release@v1 | 140 | # uses: actions/create-release@v1 |
@@ -179,11 +184,10 @@ jobs: | |||
179 | asset_name: rust-analyzer-0.1.0.vsix | 184 | asset_name: rust-analyzer-0.1.0.vsix |
180 | asset_content_type: application/octet-stream | 185 | asset_content_type: application/octet-stream |
181 | 186 | ||
182 | - uses: actions/[email protected] | 187 | - run: npm ci |
183 | env: | 188 | working-directory: ./editors/code |
184 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | 189 | |
185 | with: | 190 | - name: Publish Extension |
186 | upload_url: ${{ steps.create_release.outputs.upload_url }} | 191 | working-directory: ./editors/code |
187 | asset_path: ./dist/rust-analyzer.el | 192 | # token from https://dev.azure.com/rust-analyzer/ |
188 | asset_name: rust-analyzer.el | 193 | run: npx vsce publish 0.1.$(date +%Y%m%d) --pat ${{ secrets.MARKETPLACE_TOKEN }} |
189 | asset_content_type: text/plain | ||
diff --git a/.vscode/launch.json b/.vscode/launch.json index 55a2f10f2..b1bd98d4a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json | |||
@@ -2,39 +2,61 @@ | |||
2 | // Use IntelliSense to learn about possible attributes. | 2 | // Use IntelliSense to learn about possible attributes. |
3 | // Hover to view descriptions of existing attributes. | 3 | // Hover to view descriptions of existing attributes. |
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 |
5 | |||
6 | // NOTE: --disable-extensions | ||
7 | // Disable all installed extensions to increase performance of the debug instance | ||
8 | // and prevent potential conflicts with other installed extensions. | ||
9 | |||
5 | "version": "0.2.0", | 10 | "version": "0.2.0", |
6 | "configurations": [ | 11 | "configurations": [ |
7 | { | 12 | { |
13 | // Used for testing the extension with the installed LSP server. | ||
8 | "name": "Run Extension", | 14 | "name": "Run Extension", |
9 | "type": "extensionHost", | 15 | "type": "extensionHost", |
10 | "request": "launch", | 16 | "request": "launch", |
11 | "runtimeExecutable": "${execPath}", | 17 | "runtimeExecutable": "${execPath}", |
12 | "args": [ | 18 | "args": [ |
19 | "--disable-extensions", | ||
13 | "--extensionDevelopmentPath=${workspaceFolder}/editors/code" | 20 | "--extensionDevelopmentPath=${workspaceFolder}/editors/code" |
14 | ], | 21 | ], |
15 | "outFiles": [ | 22 | "outFiles": [ |
16 | "${workspaceFolder}/editors/code/out/**/*.js" | 23 | "${workspaceFolder}/editors/code/out/**/*.js" |
17 | ], | 24 | ], |
18 | "preLaunchTask": "Build Extension" | 25 | "preLaunchTask": "Build Extension", |
26 | "skipFiles": [ | ||
27 | "<node_internals>/**/*.js" | ||
28 | ] | ||
19 | }, | 29 | }, |
20 | { | 30 | { |
31 | // Used for testing the extension with a local build of the LSP server (in `target/debug`). | ||
21 | "name": "Run Extension (Dev Server)", | 32 | "name": "Run Extension (Dev Server)", |
22 | "type": "extensionHost", | 33 | "type": "extensionHost", |
23 | "request": "launch", | 34 | "request": "launch", |
24 | "runtimeExecutable": "${execPath}", | 35 | "runtimeExecutable": "${execPath}", |
25 | "args": [ | 36 | "args": [ |
37 | "--disable-extensions", | ||
26 | "--extensionDevelopmentPath=${workspaceFolder}/editors/code" | 38 | "--extensionDevelopmentPath=${workspaceFolder}/editors/code" |
27 | ], | 39 | ], |
28 | "outFiles": [ | 40 | "outFiles": [ |
29 | "${workspaceFolder}/editors/code/out/**/*.js" | 41 | "${workspaceFolder}/editors/code/out/**/*.js" |
30 | ], | 42 | ], |
31 | "preLaunchTask": "Build Extension", | 43 | "preLaunchTask": "Build Extension", |
44 | "skipFiles": [ | ||
45 | "<node_internals>/**/*.js" | ||
46 | ], | ||
32 | "env": { | 47 | "env": { |
33 | "__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/debug/ra_lsp_server" | 48 | "__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/debug/ra_lsp_server" |
34 | } | 49 | } |
35 | }, | 50 | }, |
36 | { | 51 | { |
37 | "name": "Debug Lsp Server", | 52 | // Used to attach LLDB to a running LSP server. |
53 | // NOTE: Might require root permissions. For this run: | ||
54 | // | ||
55 | // `echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope` | ||
56 | // | ||
57 | // Don't forget to set `debug = 2` in `Cargo.toml` before building the server | ||
58 | |||
59 | "name": "Attach To Server", | ||
38 | "type": "lldb", | 60 | "type": "lldb", |
39 | "request": "attach", | 61 | "request": "attach", |
40 | "program": "${workspaceFolder}/target/debug/ra_lsp_server", | 62 | "program": "${workspaceFolder}/target/debug/ra_lsp_server", |
diff --git a/Cargo.lock b/Cargo.lock index e29ff898d..f44e514dd 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -110,9 +110,9 @@ dependencies = [ | |||
110 | 110 | ||
111 | [[package]] | 111 | [[package]] |
112 | name = "byteorder" | 112 | name = "byteorder" |
113 | version = "1.3.2" | 113 | version = "1.3.4" |
114 | source = "registry+https://github.com/rust-lang/crates.io-index" | 114 | source = "registry+https://github.com/rust-lang/crates.io-index" |
115 | checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" | 115 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" |
116 | 116 | ||
117 | [[package]] | 117 | [[package]] |
118 | name = "c2-chacha" | 118 | name = "c2-chacha" |
@@ -1015,6 +1015,7 @@ name = "ra_cli" | |||
1015 | version = "0.1.0" | 1015 | version = "0.1.0" |
1016 | dependencies = [ | 1016 | dependencies = [ |
1017 | "env_logger", | 1017 | "env_logger", |
1018 | "itertools", | ||
1018 | "pico-args", | 1019 | "pico-args", |
1019 | "ra_batch", | 1020 | "ra_batch", |
1020 | "ra_db", | 1021 | "ra_db", |
@@ -1024,6 +1025,7 @@ dependencies = [ | |||
1024 | "ra_ide", | 1025 | "ra_ide", |
1025 | "ra_prof", | 1026 | "ra_prof", |
1026 | "ra_syntax", | 1027 | "ra_syntax", |
1028 | "rand 0.7.3", | ||
1027 | ] | 1029 | ] |
1028 | 1030 | ||
1029 | [[package]] | 1031 | [[package]] |
@@ -1070,6 +1072,7 @@ dependencies = [ | |||
1070 | "drop_bomb", | 1072 | "drop_bomb", |
1071 | "either", | 1073 | "either", |
1072 | "insta", | 1074 | "insta", |
1075 | "itertools", | ||
1073 | "log", | 1076 | "log", |
1074 | "once_cell", | 1077 | "once_cell", |
1075 | "ra_arena", | 1078 | "ra_arena", |
@@ -1173,7 +1176,6 @@ dependencies = [ | |||
1173 | "ra_prof", | 1176 | "ra_prof", |
1174 | "ra_syntax", | 1177 | "ra_syntax", |
1175 | "ra_text_edit", | 1178 | "ra_text_edit", |
1176 | "rand 0.7.3", | ||
1177 | "rayon", | 1179 | "rayon", |
1178 | "rustc-hash", | 1180 | "rustc-hash", |
1179 | "superslice", | 1181 | "superslice", |
@@ -1246,6 +1248,7 @@ dependencies = [ | |||
1246 | name = "ra_project_model" | 1248 | name = "ra_project_model" |
1247 | version = "0.1.0" | 1249 | version = "0.1.0" |
1248 | dependencies = [ | 1250 | dependencies = [ |
1251 | "anyhow", | ||
1249 | "cargo_metadata", | 1252 | "cargo_metadata", |
1250 | "log", | 1253 | "log", |
1251 | "ra_arena", | 1254 | "ra_arena", |
@@ -1563,12 +1566,9 @@ checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" | |||
1563 | 1566 | ||
1564 | [[package]] | 1567 | [[package]] |
1565 | name = "rustc-hash" | 1568 | name = "rustc-hash" |
1566 | version = "1.0.1" | 1569 | version = "1.1.0" |
1567 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1570 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1568 | checksum = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" | 1571 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" |
1569 | dependencies = [ | ||
1570 | "byteorder", | ||
1571 | ] | ||
1572 | 1572 | ||
1573 | [[package]] | 1573 | [[package]] |
1574 | name = "rustc_lexer" | 1574 | name = "rustc_lexer" |
@@ -1675,9 +1675,9 @@ dependencies = [ | |||
1675 | 1675 | ||
1676 | [[package]] | 1676 | [[package]] |
1677 | name = "serde_json" | 1677 | name = "serde_json" |
1678 | version = "1.0.46" | 1678 | version = "1.0.47" |
1679 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1679 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1680 | checksum = "21b01d7f0288608a01dca632cf1df859df6fd6ffa885300fc275ce2ba6221953" | 1680 | checksum = "15913895b61e0be854afd32fd4163fcd2a3df34142cf2cb961b310ce694cbf90" |
1681 | dependencies = [ | 1681 | dependencies = [ |
1682 | "itoa", | 1682 | "itoa", |
1683 | "ryu", | 1683 | "ryu", |
diff --git a/Cargo.toml b/Cargo.toml index e5620b1b7..c034e2424 100644 --- a/Cargo.toml +++ b/Cargo.toml | |||
@@ -31,3 +31,8 @@ opt-level = 0 | |||
31 | 31 | ||
32 | [patch.'crates-io'] | 32 | [patch.'crates-io'] |
33 | # rowan = { path = "../rowan" } | 33 | # rowan = { path = "../rowan" } |
34 | |||
35 | [patch.'https://github.com/rust-lang/chalk.git'] | ||
36 | # chalk-solve = { path = "../chalk/chalk-solve" } | ||
37 | # chalk-rust-ir = { path = "../chalk/chalk-rust-ir" } | ||
38 | # chalk-ir = { path = "../chalk/chalk-ir" } | ||
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 1fb701da5..c4aea2a06 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -1,10 +1,18 @@ | |||
1 | use ra_ide_db::imports_locator::ImportsLocator; | ||
2 | use ra_syntax::ast::{self, AstNode}; | ||
3 | |||
4 | use crate::{ | 1 | use crate::{ |
5 | assist_ctx::{Assist, AssistCtx}, | 2 | assist_ctx::{Assist, AssistCtx}, |
6 | insert_use_statement, AssistId, | 3 | insert_use_statement, AssistId, |
7 | }; | 4 | }; |
5 | use hir::{ | ||
6 | db::HirDatabase, AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, | ||
7 | SourceAnalyzer, Trait, Type, | ||
8 | }; | ||
9 | use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; | ||
10 | use ra_prof::profile; | ||
11 | use ra_syntax::{ | ||
12 | ast::{self, AstNode}, | ||
13 | SyntaxNode, | ||
14 | }; | ||
15 | use rustc_hash::FxHashSet; | ||
8 | use std::collections::BTreeSet; | 16 | use std::collections::BTreeSet; |
9 | 17 | ||
10 | // Assist: auto_import | 18 | // Assist: auto_import |
@@ -27,52 +35,24 @@ use std::collections::BTreeSet; | |||
27 | // # pub mod std { pub mod collections { pub struct HashMap { } } } | 35 | // # pub mod std { pub mod collections { pub struct HashMap { } } } |
28 | // ``` | 36 | // ``` |
29 | pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { | 37 | pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { |
30 | let path_under_caret: ast::Path = ctx.find_node_at_offset()?; | 38 | let auto_import_assets = AutoImportAssets::new(&ctx)?; |
31 | if path_under_caret.syntax().ancestors().find_map(ast::UseItem::cast).is_some() { | 39 | let proposed_imports = auto_import_assets.search_for_imports(ctx.db); |
32 | return None; | ||
33 | } | ||
34 | |||
35 | let module = path_under_caret.syntax().ancestors().find_map(ast::Module::cast); | ||
36 | let position = match module.and_then(|it| it.item_list()) { | ||
37 | Some(item_list) => item_list.syntax().clone(), | ||
38 | None => { | ||
39 | let current_file = | ||
40 | path_under_caret.syntax().ancestors().find_map(ast::SourceFile::cast)?; | ||
41 | current_file.syntax().clone() | ||
42 | } | ||
43 | }; | ||
44 | let source_analyzer = ctx.source_analyzer(&position, None); | ||
45 | let module_with_name_to_import = source_analyzer.module()?; | ||
46 | |||
47 | let name_ref_to_import = | ||
48 | path_under_caret.syntax().descendants().find_map(ast::NameRef::cast)?; | ||
49 | if source_analyzer | ||
50 | .resolve_path(ctx.db, &name_ref_to_import.syntax().ancestors().find_map(ast::Path::cast)?) | ||
51 | .is_some() | ||
52 | { | ||
53 | return None; | ||
54 | } | ||
55 | |||
56 | let name_to_import = name_ref_to_import.syntax().to_string(); | ||
57 | let proposed_imports = ImportsLocator::new(ctx.db) | ||
58 | .find_imports(&name_to_import) | ||
59 | .into_iter() | ||
60 | .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def)) | ||
61 | .filter(|use_path| !use_path.segments.is_empty()) | ||
62 | .take(20) | ||
63 | .collect::<BTreeSet<_>>(); | ||
64 | |||
65 | if proposed_imports.is_empty() { | 40 | if proposed_imports.is_empty() { |
66 | return None; | 41 | return None; |
67 | } | 42 | } |
68 | 43 | ||
69 | let mut group = ctx.add_assist_group(format!("Import {}", name_to_import)); | 44 | let assist_group_name = if proposed_imports.len() == 1 { |
45 | format!("Import `{}`", proposed_imports.iter().next().unwrap()) | ||
46 | } else { | ||
47 | auto_import_assets.get_import_group_message() | ||
48 | }; | ||
49 | let mut group = ctx.add_assist_group(assist_group_name); | ||
70 | for import in proposed_imports { | 50 | for import in proposed_imports { |
71 | group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { | 51 | group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { |
72 | edit.target(path_under_caret.syntax().text_range()); | 52 | edit.target(auto_import_assets.syntax_under_caret.text_range()); |
73 | insert_use_statement( | 53 | insert_use_statement( |
74 | &position, | 54 | &auto_import_assets.syntax_under_caret, |
75 | path_under_caret.syntax(), | 55 | &auto_import_assets.syntax_under_caret, |
76 | &import, | 56 | &import, |
77 | edit.text_edit_builder(), | 57 | edit.text_edit_builder(), |
78 | ); | 58 | ); |
@@ -81,11 +61,232 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { | |||
81 | group.finish() | 61 | group.finish() |
82 | } | 62 | } |
83 | 63 | ||
64 | struct AutoImportAssets { | ||
65 | import_candidate: ImportCandidate, | ||
66 | module_with_name_to_import: Module, | ||
67 | syntax_under_caret: SyntaxNode, | ||
68 | } | ||
69 | |||
70 | impl AutoImportAssets { | ||
71 | fn new(ctx: &AssistCtx) -> Option<Self> { | ||
72 | if let Some(path_under_caret) = ctx.find_node_at_offset::<ast::Path>() { | ||
73 | Self::for_regular_path(path_under_caret, &ctx) | ||
74 | } else { | ||
75 | Self::for_method_call(ctx.find_node_at_offset()?, &ctx) | ||
76 | } | ||
77 | } | ||
78 | |||
79 | fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> { | ||
80 | let syntax_under_caret = method_call.syntax().to_owned(); | ||
81 | let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); | ||
82 | let module_with_name_to_import = source_analyzer.module()?; | ||
83 | Some(Self { | ||
84 | import_candidate: ImportCandidate::for_method_call( | ||
85 | &method_call, | ||
86 | &source_analyzer, | ||
87 | ctx.db, | ||
88 | )?, | ||
89 | module_with_name_to_import, | ||
90 | syntax_under_caret, | ||
91 | }) | ||
92 | } | ||
93 | |||
94 | fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistCtx) -> Option<Self> { | ||
95 | let syntax_under_caret = path_under_caret.syntax().to_owned(); | ||
96 | if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() { | ||
97 | return None; | ||
98 | } | ||
99 | |||
100 | let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); | ||
101 | let module_with_name_to_import = source_analyzer.module()?; | ||
102 | Some(Self { | ||
103 | import_candidate: ImportCandidate::for_regular_path( | ||
104 | &path_under_caret, | ||
105 | &source_analyzer, | ||
106 | ctx.db, | ||
107 | )?, | ||
108 | module_with_name_to_import, | ||
109 | syntax_under_caret, | ||
110 | }) | ||
111 | } | ||
112 | |||
113 | fn get_search_query(&self) -> &str { | ||
114 | match &self.import_candidate { | ||
115 | ImportCandidate::UnqualifiedName(name) => name, | ||
116 | ImportCandidate::QualifierStart(qualifier_start) => qualifier_start, | ||
117 | ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => trait_assoc_item_name, | ||
118 | ImportCandidate::TraitMethod(_, trait_method_name) => trait_method_name, | ||
119 | } | ||
120 | } | ||
121 | |||
122 | fn get_import_group_message(&self) -> String { | ||
123 | match &self.import_candidate { | ||
124 | ImportCandidate::UnqualifiedName(name) => format!("Import {}", name), | ||
125 | ImportCandidate::QualifierStart(qualifier_start) => { | ||
126 | format!("Import {}", qualifier_start) | ||
127 | } | ||
128 | ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => { | ||
129 | format!("Import a trait for item {}", trait_assoc_item_name) | ||
130 | } | ||
131 | ImportCandidate::TraitMethod(_, trait_method_name) => { | ||
132 | format!("Import a trait for method {}", trait_method_name) | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { | ||
138 | let _p = profile("auto_import::search_for_imports"); | ||
139 | let current_crate = self.module_with_name_to_import.krate(); | ||
140 | ImportsLocator::new(db) | ||
141 | .find_imports(&self.get_search_query()) | ||
142 | .into_iter() | ||
143 | .filter_map(|module_def| match &self.import_candidate { | ||
144 | ImportCandidate::TraitAssocItem(assoc_item_type, _) => { | ||
145 | let located_assoc_item = match module_def { | ||
146 | ModuleDef::Function(located_function) => located_function | ||
147 | .as_assoc_item(db) | ||
148 | .map(|assoc| assoc.container(db)) | ||
149 | .and_then(Self::assoc_to_trait), | ||
150 | ModuleDef::Const(located_const) => located_const | ||
151 | .as_assoc_item(db) | ||
152 | .map(|assoc| assoc.container(db)) | ||
153 | .and_then(Self::assoc_to_trait), | ||
154 | _ => None, | ||
155 | }?; | ||
156 | |||
157 | let mut trait_candidates = FxHashSet::default(); | ||
158 | trait_candidates.insert(located_assoc_item.into()); | ||
159 | |||
160 | assoc_item_type | ||
161 | .iterate_path_candidates( | ||
162 | db, | ||
163 | current_crate, | ||
164 | &trait_candidates, | ||
165 | None, | ||
166 | |_, assoc| Self::assoc_to_trait(assoc.container(db)), | ||
167 | ) | ||
168 | .map(ModuleDef::from) | ||
169 | } | ||
170 | ImportCandidate::TraitMethod(function_callee, _) => { | ||
171 | let located_assoc_item = | ||
172 | if let ModuleDef::Function(located_function) = module_def { | ||
173 | located_function | ||
174 | .as_assoc_item(db) | ||
175 | .map(|assoc| assoc.container(db)) | ||
176 | .and_then(Self::assoc_to_trait) | ||
177 | } else { | ||
178 | None | ||
179 | }?; | ||
180 | |||
181 | let mut trait_candidates = FxHashSet::default(); | ||
182 | trait_candidates.insert(located_assoc_item.into()); | ||
183 | |||
184 | function_callee | ||
185 | .iterate_method_candidates( | ||
186 | db, | ||
187 | current_crate, | ||
188 | &trait_candidates, | ||
189 | None, | ||
190 | |_, function| { | ||
191 | Self::assoc_to_trait(function.as_assoc_item(db)?.container(db)) | ||
192 | }, | ||
193 | ) | ||
194 | .map(ModuleDef::from) | ||
195 | } | ||
196 | _ => Some(module_def), | ||
197 | }) | ||
198 | .filter_map(|module_def| self.module_with_name_to_import.find_use_path(db, module_def)) | ||
199 | .filter(|use_path| !use_path.segments.is_empty()) | ||
200 | .take(20) | ||
201 | .collect::<BTreeSet<_>>() | ||
202 | } | ||
203 | |||
204 | fn assoc_to_trait(assoc: AssocItemContainer) -> Option<Trait> { | ||
205 | if let AssocItemContainer::Trait(extracted_trait) = assoc { | ||
206 | Some(extracted_trait) | ||
207 | } else { | ||
208 | None | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | |||
213 | #[derive(Debug)] | ||
214 | enum ImportCandidate { | ||
215 | /// Simple name like 'HashMap' | ||
216 | UnqualifiedName(String), | ||
217 | /// First part of the qualified name. | ||
218 | /// For 'std::collections::HashMap', that will be 'std'. | ||
219 | QualifierStart(String), | ||
220 | /// A trait associated function (with no self parameter) or associated constant. | ||
221 | /// For 'test_mod::TestEnum::test_function', `Type` is the `test_mod::TestEnum` expression type | ||
222 | /// and `String` is the `test_function` | ||
223 | TraitAssocItem(Type, String), | ||
224 | /// A trait method with self parameter. | ||
225 | /// For 'test_enum.test_method()', `Type` is the `test_enum` expression type | ||
226 | /// and `String` is the `test_method` | ||
227 | TraitMethod(Type, String), | ||
228 | } | ||
229 | |||
230 | impl ImportCandidate { | ||
231 | fn for_method_call( | ||
232 | method_call: &ast::MethodCallExpr, | ||
233 | source_analyzer: &SourceAnalyzer, | ||
234 | db: &impl HirDatabase, | ||
235 | ) -> Option<Self> { | ||
236 | if source_analyzer.resolve_method_call(method_call).is_some() { | ||
237 | return None; | ||
238 | } | ||
239 | Some(Self::TraitMethod( | ||
240 | source_analyzer.type_of(db, &method_call.expr()?)?, | ||
241 | method_call.name_ref()?.syntax().to_string(), | ||
242 | )) | ||
243 | } | ||
244 | |||
245 | fn for_regular_path( | ||
246 | path_under_caret: &ast::Path, | ||
247 | source_analyzer: &SourceAnalyzer, | ||
248 | db: &impl HirDatabase, | ||
249 | ) -> Option<Self> { | ||
250 | if source_analyzer.resolve_path(db, path_under_caret).is_some() { | ||
251 | return None; | ||
252 | } | ||
253 | |||
254 | let segment = path_under_caret.segment()?; | ||
255 | if let Some(qualifier) = path_under_caret.qualifier() { | ||
256 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | ||
257 | let qualifier_start_path = | ||
258 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | ||
259 | if let Some(qualifier_start_resolution) = | ||
260 | source_analyzer.resolve_path(db, &qualifier_start_path) | ||
261 | { | ||
262 | let qualifier_resolution = if qualifier_start_path == qualifier { | ||
263 | qualifier_start_resolution | ||
264 | } else { | ||
265 | source_analyzer.resolve_path(db, &qualifier)? | ||
266 | }; | ||
267 | if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution { | ||
268 | Some(ImportCandidate::TraitAssocItem( | ||
269 | assoc_item_path.ty(db), | ||
270 | segment.syntax().to_string(), | ||
271 | )) | ||
272 | } else { | ||
273 | None | ||
274 | } | ||
275 | } else { | ||
276 | Some(ImportCandidate::QualifierStart(qualifier_start.syntax().to_string())) | ||
277 | } | ||
278 | } else { | ||
279 | Some(ImportCandidate::UnqualifiedName( | ||
280 | segment.syntax().descendants().find_map(ast::NameRef::cast)?.syntax().to_string(), | ||
281 | )) | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
84 | #[cfg(test)] | 286 | #[cfg(test)] |
85 | mod tests { | 287 | mod tests { |
86 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; | ||
87 | |||
88 | use super::*; | 288 | use super::*; |
289 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; | ||
89 | 290 | ||
90 | #[test] | 291 | #[test] |
91 | fn applicable_when_found_an_import() { | 292 | fn applicable_when_found_an_import() { |
@@ -290,4 +491,303 @@ mod tests { | |||
290 | ", | 491 | ", |
291 | ); | 492 | ); |
292 | } | 493 | } |
494 | |||
495 | #[test] | ||
496 | fn not_applicable_for_imported_function() { | ||
497 | check_assist_not_applicable( | ||
498 | auto_import, | ||
499 | r" | ||
500 | pub mod test_mod { | ||
501 | pub fn test_function() {} | ||
502 | } | ||
503 | |||
504 | use test_mod::test_function; | ||
505 | fn main() { | ||
506 | test_function<|> | ||
507 | } | ||
508 | ", | ||
509 | ); | ||
510 | } | ||
511 | |||
512 | #[test] | ||
513 | fn associated_struct_function() { | ||
514 | check_assist( | ||
515 | auto_import, | ||
516 | r" | ||
517 | mod test_mod { | ||
518 | pub struct TestStruct {} | ||
519 | impl TestStruct { | ||
520 | pub fn test_function() {} | ||
521 | } | ||
522 | } | ||
523 | |||
524 | fn main() { | ||
525 | TestStruct::test_function<|> | ||
526 | } | ||
527 | ", | ||
528 | r" | ||
529 | use test_mod::TestStruct; | ||
530 | |||
531 | mod test_mod { | ||
532 | pub struct TestStruct {} | ||
533 | impl TestStruct { | ||
534 | pub fn test_function() {} | ||
535 | } | ||
536 | } | ||
537 | |||
538 | fn main() { | ||
539 | TestStruct::test_function<|> | ||
540 | } | ||
541 | ", | ||
542 | ); | ||
543 | } | ||
544 | |||
545 | #[test] | ||
546 | fn associated_struct_const() { | ||
547 | check_assist( | ||
548 | auto_import, | ||
549 | r" | ||
550 | mod test_mod { | ||
551 | pub struct TestStruct {} | ||
552 | impl TestStruct { | ||
553 | const TEST_CONST: u8 = 42; | ||
554 | } | ||
555 | } | ||
556 | |||
557 | fn main() { | ||
558 | TestStruct::TEST_CONST<|> | ||
559 | } | ||
560 | ", | ||
561 | r" | ||
562 | use test_mod::TestStruct; | ||
563 | |||
564 | mod test_mod { | ||
565 | pub struct TestStruct {} | ||
566 | impl TestStruct { | ||
567 | const TEST_CONST: u8 = 42; | ||
568 | } | ||
569 | } | ||
570 | |||
571 | fn main() { | ||
572 | TestStruct::TEST_CONST<|> | ||
573 | } | ||
574 | ", | ||
575 | ); | ||
576 | } | ||
577 | |||
578 | #[test] | ||
579 | fn associated_trait_function() { | ||
580 | check_assist( | ||
581 | auto_import, | ||
582 | r" | ||
583 | mod test_mod { | ||
584 | pub trait TestTrait { | ||
585 | fn test_function(); | ||
586 | } | ||
587 | pub struct TestStruct {} | ||
588 | impl TestTrait for TestStruct { | ||
589 | fn test_function() {} | ||
590 | } | ||
591 | } | ||
592 | |||
593 | fn main() { | ||
594 | test_mod::TestStruct::test_function<|> | ||
595 | } | ||
596 | ", | ||
597 | r" | ||
598 | use test_mod::TestTrait; | ||
599 | |||
600 | mod test_mod { | ||
601 | pub trait TestTrait { | ||
602 | fn test_function(); | ||
603 | } | ||
604 | pub struct TestStruct {} | ||
605 | impl TestTrait for TestStruct { | ||
606 | fn test_function() {} | ||
607 | } | ||
608 | } | ||
609 | |||
610 | fn main() { | ||
611 | test_mod::TestStruct::test_function<|> | ||
612 | } | ||
613 | ", | ||
614 | ); | ||
615 | } | ||
616 | |||
617 | #[test] | ||
618 | fn not_applicable_for_imported_trait_for_function() { | ||
619 | check_assist_not_applicable( | ||
620 | auto_import, | ||
621 | r" | ||
622 | mod test_mod { | ||
623 | pub trait TestTrait { | ||
624 | fn test_function(); | ||
625 | } | ||
626 | pub trait TestTrait2 { | ||
627 | fn test_function(); | ||
628 | } | ||
629 | pub enum TestEnum { | ||
630 | One, | ||
631 | Two, | ||
632 | } | ||
633 | impl TestTrait2 for TestEnum { | ||
634 | fn test_function() {} | ||
635 | } | ||
636 | impl TestTrait for TestEnum { | ||
637 | fn test_function() {} | ||
638 | } | ||
639 | } | ||
640 | |||
641 | use test_mod::TestTrait2; | ||
642 | fn main() { | ||
643 | test_mod::TestEnum::test_function<|>; | ||
644 | } | ||
645 | ", | ||
646 | ) | ||
647 | } | ||
648 | |||
649 | #[test] | ||
650 | fn associated_trait_const() { | ||
651 | check_assist( | ||
652 | auto_import, | ||
653 | r" | ||
654 | mod test_mod { | ||
655 | pub trait TestTrait { | ||
656 | const TEST_CONST: u8; | ||
657 | } | ||
658 | pub struct TestStruct {} | ||
659 | impl TestTrait for TestStruct { | ||
660 | const TEST_CONST: u8 = 42; | ||
661 | } | ||
662 | } | ||
663 | |||
664 | fn main() { | ||
665 | test_mod::TestStruct::TEST_CONST<|> | ||
666 | } | ||
667 | ", | ||
668 | r" | ||
669 | use test_mod::TestTrait; | ||
670 | |||
671 | mod test_mod { | ||
672 | pub trait TestTrait { | ||
673 | const TEST_CONST: u8; | ||
674 | } | ||
675 | pub struct TestStruct {} | ||
676 | impl TestTrait for TestStruct { | ||
677 | const TEST_CONST: u8 = 42; | ||
678 | } | ||
679 | } | ||
680 | |||
681 | fn main() { | ||
682 | test_mod::TestStruct::TEST_CONST<|> | ||
683 | } | ||
684 | ", | ||
685 | ); | ||
686 | } | ||
687 | |||
688 | #[test] | ||
689 | fn not_applicable_for_imported_trait_for_const() { | ||
690 | check_assist_not_applicable( | ||
691 | auto_import, | ||
692 | r" | ||
693 | mod test_mod { | ||
694 | pub trait TestTrait { | ||
695 | const TEST_CONST: u8; | ||
696 | } | ||
697 | pub trait TestTrait2 { | ||
698 | const TEST_CONST: f64; | ||
699 | } | ||
700 | pub enum TestEnum { | ||
701 | One, | ||
702 | Two, | ||
703 | } | ||
704 | impl TestTrait2 for TestEnum { | ||
705 | const TEST_CONST: f64 = 42.0; | ||
706 | } | ||
707 | impl TestTrait for TestEnum { | ||
708 | const TEST_CONST: u8 = 42; | ||
709 | } | ||
710 | } | ||
711 | |||
712 | use test_mod::TestTrait2; | ||
713 | fn main() { | ||
714 | test_mod::TestEnum::TEST_CONST<|>; | ||
715 | } | ||
716 | ", | ||
717 | ) | ||
718 | } | ||
719 | |||
720 | #[test] | ||
721 | fn trait_method() { | ||
722 | check_assist( | ||
723 | auto_import, | ||
724 | r" | ||
725 | mod test_mod { | ||
726 | pub trait TestTrait { | ||
727 | fn test_method(&self); | ||
728 | } | ||
729 | pub struct TestStruct {} | ||
730 | impl TestTrait for TestStruct { | ||
731 | fn test_method(&self) {} | ||
732 | } | ||
733 | } | ||
734 | |||
735 | fn main() { | ||
736 | let test_struct = test_mod::TestStruct {}; | ||
737 | test_struct.test_meth<|>od() | ||
738 | } | ||
739 | ", | ||
740 | r" | ||
741 | use test_mod::TestTrait; | ||
742 | |||
743 | mod test_mod { | ||
744 | pub trait TestTrait { | ||
745 | fn test_method(&self); | ||
746 | } | ||
747 | pub struct TestStruct {} | ||
748 | impl TestTrait for TestStruct { | ||
749 | fn test_method(&self) {} | ||
750 | } | ||
751 | } | ||
752 | |||
753 | fn main() { | ||
754 | let test_struct = test_mod::TestStruct {}; | ||
755 | test_struct.test_meth<|>od() | ||
756 | } | ||
757 | ", | ||
758 | ); | ||
759 | } | ||
760 | |||
761 | #[test] | ||
762 | fn not_applicable_for_imported_trait_for_method() { | ||
763 | check_assist_not_applicable( | ||
764 | auto_import, | ||
765 | r" | ||
766 | mod test_mod { | ||
767 | pub trait TestTrait { | ||
768 | fn test_method(&self); | ||
769 | } | ||
770 | pub trait TestTrait2 { | ||
771 | fn test_method(&self); | ||
772 | } | ||
773 | pub enum TestEnum { | ||
774 | One, | ||
775 | Two, | ||
776 | } | ||
777 | impl TestTrait2 for TestEnum { | ||
778 | fn test_method(&self) {} | ||
779 | } | ||
780 | impl TestTrait for TestEnum { | ||
781 | fn test_method(&self) {} | ||
782 | } | ||
783 | } | ||
784 | |||
785 | use test_mod::TestTrait2; | ||
786 | fn main() { | ||
787 | let one = test_mod::TestEnum::One; | ||
788 | one.test<|>_method(); | ||
789 | } | ||
790 | ", | ||
791 | ) | ||
792 | } | ||
293 | } | 793 | } |
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index 0908fc246..ae2437ed3 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs | |||
@@ -75,10 +75,10 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> { | |||
75 | } | 75 | } |
76 | 76 | ||
77 | fn is_trivial(arm: &ast::MatchArm) -> bool { | 77 | fn is_trivial(arm: &ast::MatchArm) -> bool { |
78 | arm.pats().any(|pat| match pat { | 78 | match arm.pat() { |
79 | ast::Pat::PlaceholderPat(..) => true, | 79 | Some(ast::Pat::PlaceholderPat(..)) => true, |
80 | _ => false, | 80 | _ => false, |
81 | }) | 81 | } |
82 | } | 82 | } |
83 | 83 | ||
84 | fn resolve_enum_def( | 84 | fn resolve_enum_def( |
diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs index 670614dd8..b2a194cb5 100644 --- a/crates/ra_assists/src/handlers/merge_match_arms.rs +++ b/crates/ra_assists/src/handlers/merge_match_arms.rs | |||
@@ -75,7 +75,7 @@ pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option<Assist> { | |||
75 | } else { | 75 | } else { |
76 | arms_to_merge | 76 | arms_to_merge |
77 | .iter() | 77 | .iter() |
78 | .flat_map(ast::MatchArm::pats) | 78 | .filter_map(ast::MatchArm::pat) |
79 | .map(|x| x.syntax().to_string()) | 79 | .map(|x| x.syntax().to_string()) |
80 | .collect::<Vec<String>>() | 80 | .collect::<Vec<String>>() |
81 | .join(" | ") | 81 | .join(" | ") |
@@ -96,10 +96,10 @@ pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option<Assist> { | |||
96 | } | 96 | } |
97 | 97 | ||
98 | fn contains_placeholder(a: &ast::MatchArm) -> bool { | 98 | fn contains_placeholder(a: &ast::MatchArm) -> bool { |
99 | a.pats().any(|x| match x { | 99 | match a.pat() { |
100 | ra_syntax::ast::Pat::PlaceholderPat(..) => true, | 100 | Some(ra_syntax::ast::Pat::PlaceholderPat(..)) => true, |
101 | _ => false, | 101 | _ => false, |
102 | }) | 102 | } |
103 | } | 103 | } |
104 | 104 | ||
105 | fn next_arm(arm: &ast::MatchArm) -> Option<ast::MatchArm> { | 105 | fn next_arm(arm: &ast::MatchArm) -> Option<ast::MatchArm> { |
diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs index 2b91ce7c4..a61a2ba3e 100644 --- a/crates/ra_assists/src/handlers/move_guard.rs +++ b/crates/ra_assists/src/handlers/move_guard.rs | |||
@@ -90,7 +90,7 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option<Assist> { | |||
90 | // ``` | 90 | // ``` |
91 | pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> { | 91 | pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> { |
92 | let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; | 92 | let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; |
93 | let last_match_pat = match_arm.pats().last()?; | 93 | let match_pat = match_arm.pat()?; |
94 | 94 | ||
95 | let arm_body = match_arm.expr()?; | 95 | let arm_body = match_arm.expr()?; |
96 | let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone())?; | 96 | let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone())?; |
@@ -122,8 +122,8 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> { | |||
122 | _ => edit.replace(if_expr.syntax().text_range(), then_block.syntax().text()), | 122 | _ => edit.replace(if_expr.syntax().text_range(), then_block.syntax().text()), |
123 | } | 123 | } |
124 | 124 | ||
125 | edit.insert(last_match_pat.syntax().text_range().end(), buf); | 125 | edit.insert(match_pat.syntax().text_range().end(), buf); |
126 | edit.set_cursor(last_match_pat.syntax().text_range().end() + TextUnit::from(1)); | 126 | edit.set_cursor(match_pat.syntax().text_range().end() + TextUnit::from(1)); |
127 | }, | 127 | }, |
128 | ) | 128 | ) |
129 | } | 129 | } |
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs index b70c88ec2..eac452413 100644 --- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -431,7 +431,12 @@ fn best_action_for_target( | |||
431 | .find(|n| n.text_range().start() < anchor.text_range().start()) | 431 | .find(|n| n.text_range().start() < anchor.text_range().start()) |
432 | .or_else(|| Some(anchor)); | 432 | .or_else(|| Some(anchor)); |
433 | 433 | ||
434 | ImportAction::add_new_use(anchor, false) | 434 | let add_after_anchor = anchor |
435 | .clone() | ||
436 | .and_then(ast::Attr::cast) | ||
437 | .map(|attr| attr.kind() == ast::AttrKind::Inner) | ||
438 | .unwrap_or(false); | ||
439 | ImportAction::add_new_use(anchor, add_after_anchor) | ||
435 | } | 440 | } |
436 | } | 441 | } |
437 | } | 442 | } |
@@ -962,4 +967,26 @@ mod foo { | |||
962 | ", | 967 | ", |
963 | ); | 968 | ); |
964 | } | 969 | } |
970 | |||
971 | #[test] | ||
972 | fn inserts_imports_after_inner_attributes() { | ||
973 | check_assist( | ||
974 | replace_qualified_name_with_use, | ||
975 | " | ||
976 | #![allow(dead_code)] | ||
977 | |||
978 | fn main() { | ||
979 | std::fmt::Debug<|> | ||
980 | } | ||
981 | ", | ||
982 | " | ||
983 | #![allow(dead_code)] | ||
984 | use std::fmt::Debug; | ||
985 | |||
986 | fn main() { | ||
987 | Debug<|> | ||
988 | } | ||
989 | ", | ||
990 | ); | ||
991 | } | ||
965 | } | 992 | } |
diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index bcd408421..53d4876f6 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml | |||
@@ -6,8 +6,10 @@ authors = ["rust-analyzer developers"] | |||
6 | publish = false | 6 | publish = false |
7 | 7 | ||
8 | [dependencies] | 8 | [dependencies] |
9 | itertools = "0.8.0" | ||
9 | pico-args = "0.3.0" | 10 | pico-args = "0.3.0" |
10 | env_logger = { version = "0.7.1", default-features = false } | 11 | env_logger = { version = "0.7.1", default-features = false } |
12 | rand = { version = "0.7.0", features = ["small_rng"] } | ||
11 | 13 | ||
12 | ra_syntax = { path = "../ra_syntax" } | 14 | ra_syntax = { path = "../ra_syntax" } |
13 | ra_ide = { path = "../ra_ide" } | 15 | ra_ide = { path = "../ra_ide" } |
diff --git a/crates/ra_cli/src/analysis_bench.rs b/crates/ra_cli/src/analysis_bench.rs index 5485a38ff..4835a68ce 100644 --- a/crates/ra_cli/src/analysis_bench.rs +++ b/crates/ra_cli/src/analysis_bench.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | use std::{ | 3 | use std::{ |
4 | path::{Path, PathBuf}, | 4 | path::{Path, PathBuf}, |
5 | str::FromStr, | ||
5 | sync::Arc, | 6 | sync::Arc, |
6 | time::Instant, | 7 | time::Instant, |
7 | }; | 8 | }; |
@@ -14,12 +15,35 @@ use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FilePosition, LineCol}; | |||
14 | 15 | ||
15 | use crate::Result; | 16 | use crate::Result; |
16 | 17 | ||
18 | pub(crate) struct Position { | ||
19 | path: PathBuf, | ||
20 | line: u32, | ||
21 | column: u32, | ||
22 | } | ||
23 | |||
24 | impl FromStr for Position { | ||
25 | type Err = Box<dyn std::error::Error + Send + Sync>; | ||
26 | fn from_str(s: &str) -> Result<Self> { | ||
27 | let (path_line, column) = rsplit_at_char(s, ':')?; | ||
28 | let (path, line) = rsplit_at_char(path_line, ':')?; | ||
29 | Ok(Position { path: path.into(), line: line.parse()?, column: column.parse()? }) | ||
30 | } | ||
31 | } | ||
32 | |||
33 | fn rsplit_at_char(s: &str, c: char) -> Result<(&str, &str)> { | ||
34 | let idx = s.rfind(':').ok_or_else(|| format!("no `{}` in {}", c, s))?; | ||
35 | Ok((&s[..idx], &s[idx + 1..])) | ||
36 | } | ||
37 | |||
17 | pub(crate) enum Op { | 38 | pub(crate) enum Op { |
18 | Highlight { path: PathBuf }, | 39 | Highlight { path: PathBuf }, |
19 | Complete { path: PathBuf, line: u32, column: u32 }, | 40 | Complete(Position), |
41 | GotoDef(Position), | ||
20 | } | 42 | } |
21 | 43 | ||
22 | pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { | 44 | pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { |
45 | ra_prof::init(); | ||
46 | |||
23 | let start = Instant::now(); | 47 | let start = Instant::now(); |
24 | eprint!("loading: "); | 48 | eprint!("loading: "); |
25 | let (mut host, roots) = ra_batch::load_cargo(path)?; | 49 | let (mut host, roots) = ra_batch::load_cargo(path)?; |
@@ -29,7 +53,7 @@ pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { | |||
29 | let file_id = { | 53 | let file_id = { |
30 | let path = match &op { | 54 | let path = match &op { |
31 | Op::Highlight { path } => path, | 55 | Op::Highlight { path } => path, |
32 | Op::Complete { path, .. } => path, | 56 | Op::Complete(pos) | Op::GotoDef(pos) => &pos.path, |
33 | }; | 57 | }; |
34 | let path = std::env::current_dir()?.join(path).canonicalize()?; | 58 | let path = std::env::current_dir()?.join(path).canonicalize()?; |
35 | roots | 59 | roots |
@@ -49,7 +73,7 @@ pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { | |||
49 | .ok_or_else(|| format!("Can't find {:?}", path))? | 73 | .ok_or_else(|| format!("Can't find {:?}", path))? |
50 | }; | 74 | }; |
51 | 75 | ||
52 | match op { | 76 | match &op { |
53 | Op::Highlight { .. } => { | 77 | Op::Highlight { .. } => { |
54 | let res = do_work(&mut host, file_id, |analysis| { | 78 | let res = do_work(&mut host, file_id, |analysis| { |
55 | analysis.diagnostics(file_id).unwrap(); | 79 | analysis.diagnostics(file_id).unwrap(); |
@@ -59,16 +83,30 @@ pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { | |||
59 | println!("\n{}", res); | 83 | println!("\n{}", res); |
60 | } | 84 | } |
61 | } | 85 | } |
62 | Op::Complete { line, column, .. } => { | 86 | Op::Complete(pos) | Op::GotoDef(pos) => { |
87 | let is_completion = match op { | ||
88 | Op::Complete(..) => true, | ||
89 | _ => false, | ||
90 | }; | ||
91 | |||
63 | let offset = host | 92 | let offset = host |
64 | .analysis() | 93 | .analysis() |
65 | .file_line_index(file_id)? | 94 | .file_line_index(file_id)? |
66 | .offset(LineCol { line, col_utf16: column }); | 95 | .offset(LineCol { line: pos.line - 1, col_utf16: pos.column }); |
67 | let file_postion = FilePosition { file_id, offset }; | 96 | let file_postion = FilePosition { file_id, offset }; |
68 | 97 | ||
69 | let res = do_work(&mut host, file_id, |analysis| analysis.completions(file_postion)); | 98 | if is_completion { |
70 | if verbose { | 99 | let res = |
71 | println!("\n{:#?}", res); | 100 | do_work(&mut host, file_id, |analysis| analysis.completions(file_postion)); |
101 | if verbose { | ||
102 | println!("\n{:#?}", res); | ||
103 | } | ||
104 | } else { | ||
105 | let res = | ||
106 | do_work(&mut host, file_id, |analysis| analysis.goto_definition(file_postion)); | ||
107 | if verbose { | ||
108 | println!("\n{:#?}", res); | ||
109 | } | ||
72 | } | 110 | } |
73 | } | 111 | } |
74 | } | 112 | } |
diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs index 833235bff..6d2dd34c6 100644 --- a/crates/ra_cli/src/analysis_stats.rs +++ b/crates/ra_cli/src/analysis_stats.rs | |||
@@ -2,6 +2,9 @@ | |||
2 | 2 | ||
3 | use std::{collections::HashSet, fmt::Write, path::Path, time::Instant}; | 3 | use std::{collections::HashSet, fmt::Write, path::Path, time::Instant}; |
4 | 4 | ||
5 | use itertools::Itertools; | ||
6 | use rand::{seq::SliceRandom, thread_rng}; | ||
7 | |||
5 | use hir::{ | 8 | use hir::{ |
6 | db::{DefDatabase, HirDatabase}, | 9 | db::{DefDatabase, HirDatabase}, |
7 | AssocItem, Crate, HasSource, HirDisplay, ModuleDef, | 10 | AssocItem, Crate, HasSource, HirDisplay, ModuleDef, |
@@ -19,6 +22,7 @@ pub fn run( | |||
19 | path: &Path, | 22 | path: &Path, |
20 | only: Option<&str>, | 23 | only: Option<&str>, |
21 | with_deps: bool, | 24 | with_deps: bool, |
25 | randomize: bool, | ||
22 | ) -> Result<()> { | 26 | ) -> Result<()> { |
23 | let db_load_time = Instant::now(); | 27 | let db_load_time = Instant::now(); |
24 | let (mut host, roots) = ra_batch::load_cargo(path)?; | 28 | let (mut host, roots) = ra_batch::load_cargo(path)?; |
@@ -41,7 +45,11 @@ pub fn run( | |||
41 | }) | 45 | }) |
42 | .collect::<HashSet<_>>(); | 46 | .collect::<HashSet<_>>(); |
43 | 47 | ||
44 | for krate in Crate::all(db) { | 48 | let mut krates = Crate::all(db); |
49 | if randomize { | ||
50 | krates.shuffle(&mut thread_rng()); | ||
51 | } | ||
52 | for krate in krates { | ||
45 | let module = krate.root_module(db).expect("crate without root module"); | 53 | let module = krate.root_module(db).expect("crate without root module"); |
46 | let file_id = module.definition_source(db).file_id; | 54 | let file_id = module.definition_source(db).file_id; |
47 | if members.contains(&db.file_source_root(file_id.original_file(db))) { | 55 | if members.contains(&db.file_source_root(file_id.original_file(db))) { |
@@ -50,6 +58,10 @@ pub fn run( | |||
50 | } | 58 | } |
51 | } | 59 | } |
52 | 60 | ||
61 | if randomize { | ||
62 | visit_queue.shuffle(&mut thread_rng()); | ||
63 | } | ||
64 | |||
53 | println!("Crates in this dir: {}", num_crates); | 65 | println!("Crates in this dir: {}", num_crates); |
54 | let mut num_decls = 0; | 66 | let mut num_decls = 0; |
55 | let mut funcs = Vec::new(); | 67 | let mut funcs = Vec::new(); |
@@ -79,10 +91,14 @@ pub fn run( | |||
79 | println!("Total functions: {}", funcs.len()); | 91 | println!("Total functions: {}", funcs.len()); |
80 | println!("Item Collection: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage()); | 92 | println!("Item Collection: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage()); |
81 | 93 | ||
94 | if randomize { | ||
95 | funcs.shuffle(&mut thread_rng()); | ||
96 | } | ||
97 | |||
82 | let inference_time = Instant::now(); | 98 | let inference_time = Instant::now(); |
83 | let mut bar = match verbosity { | 99 | let mut bar = match verbosity { |
84 | Verbosity::Verbose | Verbosity::Normal => ProgressReport::new(funcs.len() as u64), | 100 | Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), |
85 | Verbosity::Quiet => ProgressReport::hidden(), | 101 | _ => ProgressReport::new(funcs.len() as u64), |
86 | }; | 102 | }; |
87 | 103 | ||
88 | bar.tick(); | 104 | bar.tick(); |
@@ -92,7 +108,20 @@ pub fn run( | |||
92 | let mut num_type_mismatches = 0; | 108 | let mut num_type_mismatches = 0; |
93 | for f in funcs { | 109 | for f in funcs { |
94 | let name = f.name(db); | 110 | let name = f.name(db); |
95 | let mut msg = format!("processing: {}", name); | 111 | let full_name = f |
112 | .module(db) | ||
113 | .path_to_root(db) | ||
114 | .into_iter() | ||
115 | .rev() | ||
116 | .filter_map(|it| it.name(db)) | ||
117 | .chain(Some(f.name(db))) | ||
118 | .join("::"); | ||
119 | if let Some(only_name) = only { | ||
120 | if name.to_string() != only_name && full_name != only_name { | ||
121 | continue; | ||
122 | } | ||
123 | } | ||
124 | let mut msg = format!("processing: {}", full_name); | ||
96 | if verbosity.is_verbose() { | 125 | if verbosity.is_verbose() { |
97 | let src = f.source(db); | 126 | let src = f.source(db); |
98 | let original_file = src.file_id.original_file(db); | 127 | let original_file = src.file_id.original_file(db); |
@@ -100,15 +129,15 @@ pub fn run( | |||
100 | let syntax_range = src.value.syntax().text_range(); | 129 | let syntax_range = src.value.syntax().text_range(); |
101 | write!(msg, " ({:?} {})", path, syntax_range).unwrap(); | 130 | write!(msg, " ({:?} {})", path, syntax_range).unwrap(); |
102 | } | 131 | } |
103 | bar.set_message(&msg); | 132 | if verbosity.is_spammy() { |
104 | if let Some(only_name) = only { | 133 | bar.println(format!("{}", msg)); |
105 | if name.to_string() != only_name { | ||
106 | continue; | ||
107 | } | ||
108 | } | 134 | } |
135 | bar.set_message(&msg); | ||
109 | let f_id = FunctionId::from(f); | 136 | let f_id = FunctionId::from(f); |
110 | let body = db.body(f_id.into()); | 137 | let body = db.body(f_id.into()); |
111 | let inference_result = db.infer(f_id.into()); | 138 | let inference_result = db.infer(f_id.into()); |
139 | let (previous_exprs, previous_unknown, previous_partially_unknown) = | ||
140 | (num_exprs, num_exprs_unknown, num_exprs_partially_unknown); | ||
112 | for (expr_id, _) in body.exprs.iter() { | 141 | for (expr_id, _) in body.exprs.iter() { |
113 | let ty = &inference_result[expr_id]; | 142 | let ty = &inference_result[expr_id]; |
114 | num_exprs += 1; | 143 | num_exprs += 1; |
@@ -125,6 +154,33 @@ pub fn run( | |||
125 | num_exprs_partially_unknown += 1; | 154 | num_exprs_partially_unknown += 1; |
126 | } | 155 | } |
127 | } | 156 | } |
157 | if only.is_some() && verbosity.is_spammy() { | ||
158 | // in super-verbose mode for just one function, we print every single expression | ||
159 | let (_, sm) = db.body_with_source_map(f_id.into()); | ||
160 | let src = sm.expr_syntax(expr_id); | ||
161 | if let Some(src) = src { | ||
162 | let original_file = src.file_id.original_file(db); | ||
163 | let line_index = host.analysis().file_line_index(original_file).unwrap(); | ||
164 | let text_range = src.value.either( | ||
165 | |it| it.syntax_node_ptr().range(), | ||
166 | |it| it.syntax_node_ptr().range(), | ||
167 | ); | ||
168 | let (start, end) = ( | ||
169 | line_index.line_col(text_range.start()), | ||
170 | line_index.line_col(text_range.end()), | ||
171 | ); | ||
172 | bar.println(format!( | ||
173 | "{}:{}-{}:{}: {}", | ||
174 | start.line + 1, | ||
175 | start.col_utf16, | ||
176 | end.line + 1, | ||
177 | end.col_utf16, | ||
178 | ty.display(db) | ||
179 | )); | ||
180 | } else { | ||
181 | bar.println(format!("unknown location: {}", ty.display(db))); | ||
182 | } | ||
183 | } | ||
128 | if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) { | 184 | if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) { |
129 | num_type_mismatches += 1; | 185 | num_type_mismatches += 1; |
130 | if verbosity.is_verbose() { | 186 | if verbosity.is_verbose() { |
@@ -164,6 +220,15 @@ pub fn run( | |||
164 | } | 220 | } |
165 | } | 221 | } |
166 | } | 222 | } |
223 | if verbosity.is_spammy() { | ||
224 | bar.println(format!( | ||
225 | "In {}: {} exprs, {} unknown, {} partial", | ||
226 | full_name, | ||
227 | num_exprs - previous_exprs, | ||
228 | num_exprs_unknown - previous_unknown, | ||
229 | num_exprs_partially_unknown - previous_partially_unknown | ||
230 | )); | ||
231 | } | ||
167 | bar.inc(1); | 232 | bar.inc(1); |
168 | } | 233 | } |
169 | bar.finish_and_clear(); | 234 | bar.finish_and_clear(); |
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs index 806612c2c..750cbab86 100644 --- a/crates/ra_cli/src/main.rs +++ b/crates/ra_cli/src/main.rs | |||
@@ -16,6 +16,7 @@ type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>; | |||
16 | 16 | ||
17 | #[derive(Clone, Copy)] | 17 | #[derive(Clone, Copy)] |
18 | pub enum Verbosity { | 18 | pub enum Verbosity { |
19 | Spammy, | ||
19 | Verbose, | 20 | Verbose, |
20 | Normal, | 21 | Normal, |
21 | Quiet, | 22 | Quiet, |
@@ -24,7 +25,13 @@ pub enum Verbosity { | |||
24 | impl Verbosity { | 25 | impl Verbosity { |
25 | fn is_verbose(self) -> bool { | 26 | fn is_verbose(self) -> bool { |
26 | match self { | 27 | match self { |
27 | Verbosity::Verbose => true, | 28 | Verbosity::Verbose | Verbosity::Spammy => true, |
29 | _ => false, | ||
30 | } | ||
31 | } | ||
32 | fn is_spammy(self) -> bool { | ||
33 | match self { | ||
34 | Verbosity::Spammy => true, | ||
28 | _ => false, | 35 | _ => false, |
29 | } | 36 | } |
30 | } | 37 | } |
@@ -86,14 +93,18 @@ fn main() -> Result<()> { | |||
86 | return Ok(()); | 93 | return Ok(()); |
87 | } | 94 | } |
88 | let verbosity = match ( | 95 | let verbosity = match ( |
96 | matches.contains(["-vv", "--spammy"]), | ||
89 | matches.contains(["-v", "--verbose"]), | 97 | matches.contains(["-v", "--verbose"]), |
90 | matches.contains(["-q", "--quiet"]), | 98 | matches.contains(["-q", "--quiet"]), |
91 | ) { | 99 | ) { |
92 | (false, false) => Verbosity::Normal, | 100 | (true, _, true) => Err("Invalid flags: -q conflicts with -vv")?, |
93 | (false, true) => Verbosity::Quiet, | 101 | (true, _, false) => Verbosity::Spammy, |
94 | (true, false) => Verbosity::Verbose, | 102 | (false, false, false) => Verbosity::Normal, |
95 | (true, true) => Err("Invalid flags: -q conflicts with -v")?, | 103 | (false, false, true) => Verbosity::Quiet, |
104 | (false, true, false) => Verbosity::Verbose, | ||
105 | (false, true, true) => Err("Invalid flags: -q conflicts with -v")?, | ||
96 | }; | 106 | }; |
107 | let randomize = matches.contains("--randomize"); | ||
97 | let memory_usage = matches.contains("--memory-usage"); | 108 | let memory_usage = matches.contains("--memory-usage"); |
98 | let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?; | 109 | let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?; |
99 | let with_deps: bool = matches.contains("--with-deps"); | 110 | let with_deps: bool = matches.contains("--with-deps"); |
@@ -111,6 +122,7 @@ fn main() -> Result<()> { | |||
111 | path.as_ref(), | 122 | path.as_ref(), |
112 | only.as_ref().map(String::as_ref), | 123 | only.as_ref().map(String::as_ref), |
113 | with_deps, | 124 | with_deps, |
125 | randomize, | ||
114 | )?; | 126 | )?; |
115 | } | 127 | } |
116 | "analysis-bench" => { | 128 | "analysis-bench" => { |
@@ -120,25 +132,16 @@ fn main() -> Result<()> { | |||
120 | } | 132 | } |
121 | let verbose = matches.contains(["-v", "--verbose"]); | 133 | let verbose = matches.contains(["-v", "--verbose"]); |
122 | let path: String = matches.opt_value_from_str("--path")?.unwrap_or_default(); | 134 | let path: String = matches.opt_value_from_str("--path")?.unwrap_or_default(); |
123 | let highlight_path = matches.opt_value_from_str("--highlight")?; | 135 | let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?; |
124 | let complete_path = matches.opt_value_from_str("--complete")?; | 136 | let complete_path: Option<String> = matches.opt_value_from_str("--complete")?; |
125 | if highlight_path.is_some() && complete_path.is_some() { | 137 | let goto_def_path: Option<String> = matches.opt_value_from_str("--goto-def")?; |
126 | panic!("either --highlight or --complete must be set, not both") | 138 | let op = match (highlight_path, complete_path, goto_def_path) { |
127 | } | 139 | (Some(path), None, None) => analysis_bench::Op::Highlight { path: path.into() }, |
128 | let op = if let Some(path) = highlight_path { | 140 | (None, Some(position), None) => analysis_bench::Op::Complete(position.parse()?), |
129 | let path: String = path; | 141 | (None, None, Some(position)) => analysis_bench::Op::GotoDef(position.parse()?), |
130 | analysis_bench::Op::Highlight { path: path.into() } | 142 | _ => panic!( |
131 | } else if let Some(path_line_col) = complete_path { | 143 | "exactly one of `--highlight`, `--complete` or `--goto-def` must be set" |
132 | let path_line_col: String = path_line_col; | 144 | ), |
133 | let (path_line, column) = rsplit_at_char(path_line_col.as_str(), ':')?; | ||
134 | let (path, line) = rsplit_at_char(path_line, ':')?; | ||
135 | analysis_bench::Op::Complete { | ||
136 | path: path.into(), | ||
137 | line: line.parse()?, | ||
138 | column: column.parse()?, | ||
139 | } | ||
140 | } else { | ||
141 | panic!("either --highlight or --complete must be set") | ||
142 | }; | 145 | }; |
143 | matches.finish().or_else(handle_extra_flags)?; | 146 | matches.finish().or_else(handle_extra_flags)?; |
144 | analysis_bench::run(verbose, path.as_ref(), op)?; | 147 | analysis_bench::run(verbose, path.as_ref(), op)?; |
@@ -171,8 +174,3 @@ fn read_stdin() -> Result<String> { | |||
171 | std::io::stdin().read_to_string(&mut buff)?; | 174 | std::io::stdin().read_to_string(&mut buff)?; |
172 | Ok(buff) | 175 | Ok(buff) |
173 | } | 176 | } |
174 | |||
175 | fn rsplit_at_char(s: &str, c: char) -> Result<(&str, &str)> { | ||
176 | let idx = s.rfind(':').ok_or_else(|| format!("no `{}` in {}", c, s))?; | ||
177 | Ok((&s[..idx], &s[idx + 1..])) | ||
178 | } | ||
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 4d9641728..a56b8ab04 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -10,9 +10,9 @@ use hir_def::{ | |||
10 | per_ns::PerNs, | 10 | per_ns::PerNs, |
11 | resolver::HasResolver, | 11 | resolver::HasResolver, |
12 | type_ref::{Mutability, TypeRef}, | 12 | type_ref::{Mutability, TypeRef}, |
13 | AdtId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, | 13 | AdtId, AssocContainerId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, |
14 | LocalEnumVariantId, LocalModuleId, LocalStructFieldId, Lookup, ModuleId, StaticId, StructId, | 14 | ImplId, LocalEnumVariantId, LocalModuleId, LocalStructFieldId, Lookup, ModuleId, StaticId, |
15 | TraitId, TypeAliasId, TypeParamId, UnionId, | 15 | StructId, TraitId, TypeAliasId, TypeParamId, UnionId, |
16 | }; | 16 | }; |
17 | use hir_expand::{ | 17 | use hir_expand::{ |
18 | diagnostics::DiagnosticSink, | 18 | diagnostics::DiagnosticSink, |
@@ -25,7 +25,10 @@ use hir_ty::{ | |||
25 | }; | 25 | }; |
26 | use ra_db::{CrateId, Edition, FileId}; | 26 | use ra_db::{CrateId, Edition, FileId}; |
27 | use ra_prof::profile; | 27 | use ra_prof::profile; |
28 | use ra_syntax::ast::{self, AttrsOwner}; | 28 | use ra_syntax::{ |
29 | ast::{self, AttrsOwner}, | ||
30 | AstNode, | ||
31 | }; | ||
29 | 32 | ||
30 | use crate::{ | 33 | use crate::{ |
31 | db::{DefDatabase, HirDatabase}, | 34 | db::{DefDatabase, HirDatabase}, |
@@ -119,7 +122,9 @@ impl_froms!( | |||
119 | BuiltinType | 122 | BuiltinType |
120 | ); | 123 | ); |
121 | 124 | ||
122 | pub use hir_def::{attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocItemId}; | 125 | pub use hir_def::{ |
126 | attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocItemId, AssocItemLoc, | ||
127 | }; | ||
123 | use rustc_hash::FxHashSet; | 128 | use rustc_hash::FxHashSet; |
124 | 129 | ||
125 | impl Module { | 130 | impl Module { |
@@ -639,17 +644,49 @@ pub struct MacroDef { | |||
639 | pub(crate) id: MacroDefId, | 644 | pub(crate) id: MacroDefId, |
640 | } | 645 | } |
641 | 646 | ||
647 | /// Invariant: `inner.as_assoc_item(db).is_some()` | ||
648 | /// We do not actively enforce this invariant. | ||
642 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 649 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
643 | pub enum AssocItem { | 650 | pub enum AssocItem { |
644 | Function(Function), | 651 | Function(Function), |
645 | Const(Const), | 652 | Const(Const), |
646 | TypeAlias(TypeAlias), | 653 | TypeAlias(TypeAlias), |
647 | } | 654 | } |
648 | // FIXME: not every function, ... is actually an assoc item. maybe we should make | 655 | pub enum AssocItemContainer { |
649 | // sure that you can only turn actual assoc items into AssocItems. This would | 656 | Trait(Trait), |
650 | // require not implementing From, and instead having some checked way of | 657 | ImplBlock(ImplBlock), |
651 | // casting them, and somehow making the constructors private, which would be annoying. | 658 | } |
652 | impl_froms!(AssocItem: Function, Const, TypeAlias); | 659 | pub trait AsAssocItem { |
660 | fn as_assoc_item(self, db: &impl DefDatabase) -> Option<AssocItem>; | ||
661 | } | ||
662 | |||
663 | impl AsAssocItem for Function { | ||
664 | fn as_assoc_item(self, db: &impl DefDatabase) -> Option<AssocItem> { | ||
665 | as_assoc_item(db, AssocItem::Function, self.id) | ||
666 | } | ||
667 | } | ||
668 | impl AsAssocItem for Const { | ||
669 | fn as_assoc_item(self, db: &impl DefDatabase) -> Option<AssocItem> { | ||
670 | as_assoc_item(db, AssocItem::Const, self.id) | ||
671 | } | ||
672 | } | ||
673 | impl AsAssocItem for TypeAlias { | ||
674 | fn as_assoc_item(self, db: &impl DefDatabase) -> Option<AssocItem> { | ||
675 | as_assoc_item(db, AssocItem::TypeAlias, self.id) | ||
676 | } | ||
677 | } | ||
678 | fn as_assoc_item<ID, DEF, CTOR, AST>(db: &impl DefDatabase, ctor: CTOR, id: ID) -> Option<AssocItem> | ||
679 | where | ||
680 | ID: Lookup<Data = AssocItemLoc<AST>>, | ||
681 | DEF: From<ID>, | ||
682 | CTOR: FnOnce(DEF) -> AssocItem, | ||
683 | AST: AstNode, | ||
684 | { | ||
685 | match id.lookup(db).container { | ||
686 | AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))), | ||
687 | AssocContainerId::ContainerId(_) => None, | ||
688 | } | ||
689 | } | ||
653 | 690 | ||
654 | impl AssocItem { | 691 | impl AssocItem { |
655 | pub fn module(self, db: &impl DefDatabase) -> Module { | 692 | pub fn module(self, db: &impl DefDatabase) -> Module { |
@@ -659,6 +696,18 @@ impl AssocItem { | |||
659 | AssocItem::TypeAlias(t) => t.module(db), | 696 | AssocItem::TypeAlias(t) => t.module(db), |
660 | } | 697 | } |
661 | } | 698 | } |
699 | pub fn container(self, db: &impl DefDatabase) -> AssocItemContainer { | ||
700 | let container = match self { | ||
701 | AssocItem::Function(it) => it.id.lookup(db).container, | ||
702 | AssocItem::Const(it) => it.id.lookup(db).container, | ||
703 | AssocItem::TypeAlias(it) => it.id.lookup(db).container, | ||
704 | }; | ||
705 | match container { | ||
706 | AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()), | ||
707 | AssocContainerId::ImplId(id) => AssocItemContainer::ImplBlock(id.into()), | ||
708 | AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), | ||
709 | } | ||
710 | } | ||
662 | } | 711 | } |
663 | 712 | ||
664 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | 713 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] |
@@ -769,6 +818,7 @@ impl TypeParam { | |||
769 | } | 818 | } |
770 | } | 819 | } |
771 | 820 | ||
821 | // FIXME: rename from `ImplBlock` to `Impl` | ||
772 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 822 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
773 | pub struct ImplBlock { | 823 | pub struct ImplBlock { |
774 | pub(crate) id: ImplId, | 824 | pub(crate) id: ImplId, |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index e1c7b7a20..5cd965f7a 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -39,10 +39,10 @@ mod has_source; | |||
39 | 39 | ||
40 | pub use crate::{ | 40 | pub use crate::{ |
41 | code_model::{ | 41 | code_model::{ |
42 | Adt, AssocItem, AttrDef, Const, Crate, CrateDependency, DefWithBody, Docs, Enum, | 42 | Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, CrateDependency, |
43 | EnumVariant, FieldSource, Function, GenericDef, HasAttrs, HasVisibility, ImplBlock, Local, | 43 | DefWithBody, Docs, Enum, EnumVariant, FieldSource, Function, GenericDef, HasAttrs, |
44 | MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias, | 44 | HasVisibility, ImplBlock, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, |
45 | TypeParam, Union, VariantDef, | 45 | StructField, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, |
46 | }, | 46 | }, |
47 | has_source::HasSource, | 47 | has_source::HasSource, |
48 | source_analyzer::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, | 48 | source_analyzer::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, |
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index bb9a35c5d..94d5b4cfd 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -15,11 +15,9 @@ use hir_def::{ | |||
15 | }, | 15 | }, |
16 | expr::{ExprId, PatId}, | 16 | expr::{ExprId, PatId}, |
17 | resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs}, | 17 | resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs}, |
18 | DefWithBodyId, TraitId, | 18 | AsMacroCall, DefWithBodyId, TraitId, |
19 | }; | ||
20 | use hir_expand::{ | ||
21 | hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, | ||
22 | }; | 19 | }; |
20 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile, MacroCallId}; | ||
23 | use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; | 21 | use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; |
24 | use ra_syntax::{ | 22 | use ra_syntax::{ |
25 | ast::{self, AstNode}, | 23 | ast::{self, AstNode}, |
@@ -363,12 +361,10 @@ impl SourceAnalyzer { | |||
363 | db: &impl HirDatabase, | 361 | db: &impl HirDatabase, |
364 | macro_call: InFile<&ast::MacroCall>, | 362 | macro_call: InFile<&ast::MacroCall>, |
365 | ) -> Option<Expansion> { | 363 | ) -> Option<Expansion> { |
366 | let def = self.resolve_macro_call(db, macro_call)?.id; | 364 | let macro_call_id = macro_call.as_call_id(db, |path| { |
367 | let ast_id = AstId::new( | 365 | self.resolver.resolve_path_as_macro(db, &path).map(|it| it.into()) |
368 | macro_call.file_id, | 366 | })?; |
369 | db.ast_id_map(macro_call.file_id).ast_id(macro_call.value), | 367 | Some(Expansion { macro_call_id }) |
370 | ); | ||
371 | Some(Expansion { macro_call_id: def.as_call_id(db, MacroCallKind::FnLike(ast_id)) }) | ||
372 | } | 368 | } |
373 | } | 369 | } |
374 | 370 | ||
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index 1efa00fe0..6b9be9948 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml | |||
@@ -14,6 +14,7 @@ rustc-hash = "1.0" | |||
14 | either = "1.5" | 14 | either = "1.5" |
15 | anymap = "0.12" | 15 | anymap = "0.12" |
16 | drop_bomb = "0.1.4" | 16 | drop_bomb = "0.1.4" |
17 | itertools = "0.8.2" | ||
17 | 18 | ||
18 | ra_arena = { path = "../ra_arena" } | 19 | ra_arena = { path = "../ra_arena" } |
19 | ra_db = { path = "../ra_db" } | 20 | ra_db = { path = "../ra_db" } |
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 142c52d35..010d35e55 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs | |||
@@ -7,9 +7,7 @@ use std::{mem, ops::Index, sync::Arc}; | |||
7 | 7 | ||
8 | use drop_bomb::DropBomb; | 8 | use drop_bomb::DropBomb; |
9 | use either::Either; | 9 | use either::Either; |
10 | use hir_expand::{ | 10 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId}; |
11 | ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroCallKind, MacroDefId, | ||
12 | }; | ||
13 | use ra_arena::{map::ArenaMap, Arena}; | 11 | use ra_arena::{map::ArenaMap, Arena}; |
14 | use ra_prof::profile; | 12 | use ra_prof::profile; |
15 | use ra_syntax::{ast, AstNode, AstPtr}; | 13 | use ra_syntax::{ast, AstNode, AstPtr}; |
@@ -23,7 +21,7 @@ use crate::{ | |||
23 | nameres::CrateDefMap, | 21 | nameres::CrateDefMap, |
24 | path::{ModPath, Path}, | 22 | path::{ModPath, Path}, |
25 | src::HasSource, | 23 | src::HasSource, |
26 | DefWithBodyId, HasModule, Lookup, ModuleId, | 24 | AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, |
27 | }; | 25 | }; |
28 | 26 | ||
29 | pub(crate) struct Expander { | 27 | pub(crate) struct Expander { |
@@ -51,30 +49,26 @@ impl Expander { | |||
51 | db: &DB, | 49 | db: &DB, |
52 | macro_call: ast::MacroCall, | 50 | macro_call: ast::MacroCall, |
53 | ) -> Option<(Mark, T)> { | 51 | ) -> Option<(Mark, T)> { |
54 | let ast_id = AstId::new( | 52 | let macro_call = InFile::new(self.current_file_id, ¯o_call); |
55 | self.current_file_id, | 53 | |
56 | db.ast_id_map(self.current_file_id).ast_id(¯o_call), | 54 | if let Some(call_id) = |
57 | ); | 55 | macro_call.as_call_id(db, |path| self.resolve_path_as_macro(db, &path)) |
58 | 56 | { | |
59 | if let Some(path) = macro_call.path().and_then(|path| self.parse_mod_path(path)) { | 57 | let file_id = call_id.as_file(); |
60 | if let Some(def) = self.resolve_path_as_macro(db, &path) { | 58 | if let Some(node) = db.parse_or_expand(file_id) { |
61 | let call_id = def.as_call_id(db, MacroCallKind::FnLike(ast_id)); | 59 | if let Some(expr) = T::cast(node) { |
62 | let file_id = call_id.as_file(); | 60 | log::debug!("macro expansion {:#?}", expr.syntax()); |
63 | if let Some(node) = db.parse_or_expand(file_id) { | 61 | |
64 | if let Some(expr) = T::cast(node) { | 62 | let mark = Mark { |
65 | log::debug!("macro expansion {:#?}", expr.syntax()); | 63 | file_id: self.current_file_id, |
66 | 64 | ast_id_map: mem::take(&mut self.ast_id_map), | |
67 | let mark = Mark { | 65 | bomb: DropBomb::new("expansion mark dropped"), |
68 | file_id: self.current_file_id, | 66 | }; |
69 | ast_id_map: mem::take(&mut self.ast_id_map), | 67 | self.hygiene = Hygiene::new(db, file_id); |
70 | bomb: DropBomb::new("expansion mark dropped"), | 68 | self.current_file_id = file_id; |
71 | }; | 69 | self.ast_id_map = db.ast_id_map(file_id); |
72 | self.hygiene = Hygiene::new(db, file_id); | 70 | |
73 | self.current_file_id = file_id; | 71 | return Some((mark, expr)); |
74 | self.ast_id_map = db.ast_id_map(file_id); | ||
75 | |||
76 | return Some((mark, expr)); | ||
77 | } | ||
78 | } | 72 | } |
79 | } | 73 | } |
80 | } | 74 | } |
@@ -99,10 +93,6 @@ impl Expander { | |||
99 | Path::from_src(path, &self.hygiene) | 93 | Path::from_src(path, &self.hygiene) |
100 | } | 94 | } |
101 | 95 | ||
102 | fn parse_mod_path(&mut self, path: ast::Path) -> Option<ModPath> { | ||
103 | ModPath::from_src(path, &self.hygiene) | ||
104 | } | ||
105 | |||
106 | fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &ModPath) -> Option<MacroDefId> { | 96 | fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &ModPath) -> Option<MacroDefId> { |
107 | self.crate_def_map | 97 | self.crate_def_map |
108 | .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other) | 98 | .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other) |
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index e656f9a41..1fc892362 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs | |||
@@ -8,7 +8,7 @@ use ra_arena::Arena; | |||
8 | use ra_syntax::{ | 8 | use ra_syntax::{ |
9 | ast::{ | 9 | ast::{ |
10 | self, ArgListOwner, ArrayExprKind, LiteralKind, LoopBodyOwner, ModuleItemOwner, NameOwner, | 10 | self, ArgListOwner, ArrayExprKind, LiteralKind, LoopBodyOwner, ModuleItemOwner, NameOwner, |
11 | TypeAscriptionOwner, | 11 | SlicePatComponents, TypeAscriptionOwner, |
12 | }, | 12 | }, |
13 | AstNode, AstPtr, | 13 | AstNode, AstPtr, |
14 | }; | 14 | }; |
@@ -164,9 +164,9 @@ where | |||
164 | let match_expr = self.collect_expr_opt(condition.expr()); | 164 | let match_expr = self.collect_expr_opt(condition.expr()); |
165 | let placeholder_pat = self.missing_pat(); | 165 | let placeholder_pat = self.missing_pat(); |
166 | let arms = vec![ | 166 | let arms = vec![ |
167 | MatchArm { pats: vec![pat], expr: then_branch, guard: None }, | 167 | MatchArm { pat, expr: then_branch, guard: None }, |
168 | MatchArm { | 168 | MatchArm { |
169 | pats: vec![placeholder_pat], | 169 | pat: placeholder_pat, |
170 | expr: else_branch.unwrap_or_else(|| self.empty_block()), | 170 | expr: else_branch.unwrap_or_else(|| self.empty_block()), |
171 | guard: None, | 171 | guard: None, |
172 | }, | 172 | }, |
@@ -203,8 +203,8 @@ where | |||
203 | let placeholder_pat = self.missing_pat(); | 203 | let placeholder_pat = self.missing_pat(); |
204 | let break_ = self.alloc_expr_desugared(Expr::Break { expr: None }); | 204 | let break_ = self.alloc_expr_desugared(Expr::Break { expr: None }); |
205 | let arms = vec![ | 205 | let arms = vec![ |
206 | MatchArm { pats: vec![pat], expr: body, guard: None }, | 206 | MatchArm { pat, expr: body, guard: None }, |
207 | MatchArm { pats: vec![placeholder_pat], expr: break_, guard: None }, | 207 | MatchArm { pat: placeholder_pat, expr: break_, guard: None }, |
208 | ]; | 208 | ]; |
209 | let match_expr = | 209 | let match_expr = |
210 | self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); | 210 | self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); |
@@ -250,7 +250,7 @@ where | |||
250 | match_arm_list | 250 | match_arm_list |
251 | .arms() | 251 | .arms() |
252 | .map(|arm| MatchArm { | 252 | .map(|arm| MatchArm { |
253 | pats: arm.pats().map(|p| self.collect_pat(p)).collect(), | 253 | pat: self.collect_pat_opt(arm.pat()), |
254 | expr: self.collect_expr_opt(arm.expr()), | 254 | expr: self.collect_expr_opt(arm.expr()), |
255 | guard: arm | 255 | guard: arm |
256 | .guard() | 256 | .guard() |
@@ -587,11 +587,16 @@ where | |||
587 | let path = p.path().and_then(|path| self.expander.parse_path(path)); | 587 | let path = p.path().and_then(|path| self.expander.parse_path(path)); |
588 | path.map(Pat::Path).unwrap_or(Pat::Missing) | 588 | path.map(Pat::Path).unwrap_or(Pat::Missing) |
589 | } | 589 | } |
590 | ast::Pat::OrPat(p) => { | ||
591 | let pats = p.pats().map(|p| self.collect_pat(p)).collect(); | ||
592 | Pat::Or(pats) | ||
593 | } | ||
594 | ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat()), | ||
590 | ast::Pat::TuplePat(p) => { | 595 | ast::Pat::TuplePat(p) => { |
591 | let args = p.args().map(|p| self.collect_pat(p)).collect(); | 596 | let args = p.args().map(|p| self.collect_pat(p)).collect(); |
592 | Pat::Tuple(args) | 597 | Pat::Tuple(args) |
593 | } | 598 | } |
594 | ast::Pat::PlaceholderPat(_) => Pat::Wild, | 599 | ast::Pat::PlaceholderPat(_) | ast::Pat::DotDotPat(_) => Pat::Wild, |
595 | ast::Pat::RecordPat(p) => { | 600 | ast::Pat::RecordPat(p) => { |
596 | let path = p.path().and_then(|path| self.expander.parse_path(path)); | 601 | let path = p.path().and_then(|path| self.expander.parse_path(path)); |
597 | let record_field_pat_list = | 602 | let record_field_pat_list = |
@@ -616,12 +621,20 @@ where | |||
616 | 621 | ||
617 | Pat::Record { path, args: fields } | 622 | Pat::Record { path, args: fields } |
618 | } | 623 | } |
624 | ast::Pat::SlicePat(p) => { | ||
625 | let SlicePatComponents { prefix, slice, suffix } = p.components(); | ||
626 | |||
627 | Pat::Slice { | ||
628 | prefix: prefix.into_iter().map(|p| self.collect_pat(p)).collect(), | ||
629 | slice: slice.map(|p| self.collect_pat(p)), | ||
630 | suffix: suffix.into_iter().map(|p| self.collect_pat(p)).collect(), | ||
631 | } | ||
632 | } | ||
619 | 633 | ||
620 | // FIXME: implement | 634 | // FIXME: implement |
621 | ast::Pat::DotDotPat(_) => Pat::Missing, | ||
622 | ast::Pat::BoxPat(_) => Pat::Missing, | 635 | ast::Pat::BoxPat(_) => Pat::Missing, |
623 | ast::Pat::LiteralPat(_) => Pat::Missing, | 636 | ast::Pat::LiteralPat(_) => Pat::Missing, |
624 | ast::Pat::SlicePat(_) | ast::Pat::RangePat(_) => Pat::Missing, | 637 | ast::Pat::RangePat(_) => Pat::Missing, |
625 | }; | 638 | }; |
626 | let ptr = AstPtr::new(&pat); | 639 | let ptr = AstPtr::new(&pat); |
627 | self.alloc_pat(pattern, Either::Left(ptr)) | 640 | self.alloc_pat(pattern, Either::Left(ptr)) |
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs index a63552327..a58a7b21f 100644 --- a/crates/ra_hir_def/src/body/scope.rs +++ b/crates/ra_hir_def/src/body/scope.rs | |||
@@ -158,9 +158,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope | |||
158 | compute_expr_scopes(*expr, body, scopes, scope); | 158 | compute_expr_scopes(*expr, body, scopes, scope); |
159 | for arm in arms { | 159 | for arm in arms { |
160 | let scope = scopes.new_scope(scope); | 160 | let scope = scopes.new_scope(scope); |
161 | for pat in &arm.pats { | 161 | scopes.add_bindings(body, scope, arm.pat); |
162 | scopes.add_bindings(body, scope, *pat); | ||
163 | } | ||
164 | scopes.set_scope(arm.expr, scope); | 162 | scopes.set_scope(arm.expr, scope); |
165 | compute_expr_scopes(arm.expr, body, scopes, scope); | 163 | compute_expr_scopes(arm.expr, body, scopes, scope); |
166 | } | 164 | } |
@@ -194,7 +192,7 @@ mod tests { | |||
194 | let (off, code) = extract_offset(code); | 192 | let (off, code) = extract_offset(code); |
195 | let code = { | 193 | let code = { |
196 | let mut buf = String::new(); | 194 | let mut buf = String::new(); |
197 | let off = u32::from(off) as usize; | 195 | let off = off.to_usize(); |
198 | buf.push_str(&code[..off]); | 196 | buf.push_str(&code[..off]); |
199 | buf.push_str("marker"); | 197 | buf.push_str("marker"); |
200 | buf.push_str(&code[off..]); | 198 | buf.push_str(&code[off..]); |
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs index a75ef9970..9707c5527 100644 --- a/crates/ra_hir_def/src/expr.rs +++ b/crates/ra_hir_def/src/expr.rs | |||
@@ -202,7 +202,7 @@ pub enum Array { | |||
202 | 202 | ||
203 | #[derive(Debug, Clone, Eq, PartialEq)] | 203 | #[derive(Debug, Clone, Eq, PartialEq)] |
204 | pub struct MatchArm { | 204 | pub struct MatchArm { |
205 | pub pats: Vec<PatId>, | 205 | pub pat: PatId, |
206 | pub guard: Option<ExprId>, | 206 | pub guard: Option<ExprId>, |
207 | pub expr: ExprId, | 207 | pub expr: ExprId, |
208 | } | 208 | } |
@@ -382,6 +382,7 @@ pub enum Pat { | |||
382 | Missing, | 382 | Missing, |
383 | Wild, | 383 | Wild, |
384 | Tuple(Vec<PatId>), | 384 | Tuple(Vec<PatId>), |
385 | Or(Vec<PatId>), | ||
385 | Record { | 386 | Record { |
386 | path: Option<Path>, | 387 | path: Option<Path>, |
387 | args: Vec<RecordFieldPat>, | 388 | args: Vec<RecordFieldPat>, |
@@ -393,7 +394,7 @@ pub enum Pat { | |||
393 | }, | 394 | }, |
394 | Slice { | 395 | Slice { |
395 | prefix: Vec<PatId>, | 396 | prefix: Vec<PatId>, |
396 | rest: Option<PatId>, | 397 | slice: Option<PatId>, |
397 | suffix: Vec<PatId>, | 398 | suffix: Vec<PatId>, |
398 | }, | 399 | }, |
399 | Path(Path), | 400 | Path(Path), |
@@ -420,12 +421,12 @@ impl Pat { | |||
420 | Pat::Bind { subpat, .. } => { | 421 | Pat::Bind { subpat, .. } => { |
421 | subpat.iter().copied().for_each(f); | 422 | subpat.iter().copied().for_each(f); |
422 | } | 423 | } |
423 | Pat::Tuple(args) | Pat::TupleStruct { args, .. } => { | 424 | Pat::Or(args) | Pat::Tuple(args) | Pat::TupleStruct { args, .. } => { |
424 | args.iter().copied().for_each(f); | 425 | args.iter().copied().for_each(f); |
425 | } | 426 | } |
426 | Pat::Ref { pat, .. } => f(*pat), | 427 | Pat::Ref { pat, .. } => f(*pat), |
427 | Pat::Slice { prefix, rest, suffix } => { | 428 | Pat::Slice { prefix, slice, suffix } => { |
428 | let total_iter = prefix.iter().chain(rest.iter()).chain(suffix.iter()); | 429 | let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter()); |
429 | total_iter.copied().for_each(f); | 430 | total_iter.copied().for_each(f); |
430 | } | 431 | } |
431 | Pat::Record { args, .. } => { | 432 | Pat::Record { args, .. } => { |
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index feb3a300d..aa0b558b8 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs | |||
@@ -46,7 +46,10 @@ mod marks; | |||
46 | 46 | ||
47 | use std::hash::Hash; | 47 | use std::hash::Hash; |
48 | 48 | ||
49 | use hir_expand::{ast_id_map::FileAstId, AstId, HirFileId, InFile, MacroDefId}; | 49 | use hir_expand::{ |
50 | ast_id_map::FileAstId, db::AstDatabase, hygiene::Hygiene, AstId, HirFileId, InFile, | ||
51 | MacroCallId, MacroCallKind, MacroDefId, | ||
52 | }; | ||
50 | use ra_arena::{impl_arena_id, RawId}; | 53 | use ra_arena::{impl_arena_id, RawId}; |
51 | use ra_db::{impl_intern_key, salsa, CrateId}; | 54 | use ra_db::{impl_intern_key, salsa, CrateId}; |
52 | use ra_syntax::{ast, AstNode}; | 55 | use ra_syntax::{ast, AstNode}; |
@@ -413,3 +416,61 @@ impl HasModule for StaticLoc { | |||
413 | self.container.module(db) | 416 | self.container.module(db) |
414 | } | 417 | } |
415 | } | 418 | } |
419 | |||
420 | /// A helper trait for converting to MacroCallId | ||
421 | pub trait AsMacroCall { | ||
422 | fn as_call_id( | ||
423 | &self, | ||
424 | db: &(impl db::DefDatabase + AstDatabase), | ||
425 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
426 | ) -> Option<MacroCallId>; | ||
427 | } | ||
428 | |||
429 | impl AsMacroCall for InFile<&ast::MacroCall> { | ||
430 | fn as_call_id( | ||
431 | &self, | ||
432 | db: &(impl db::DefDatabase + AstDatabase), | ||
433 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
434 | ) -> Option<MacroCallId> { | ||
435 | let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); | ||
436 | let h = Hygiene::new(db, self.file_id); | ||
437 | let path = path::ModPath::from_src(self.value.path()?, &h)?; | ||
438 | |||
439 | AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, resolver) | ||
440 | } | ||
441 | } | ||
442 | |||
443 | /// Helper wrapper for `AstId` with `ModPath` | ||
444 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
445 | struct AstIdWithPath<T: ast::AstNode> { | ||
446 | pub ast_id: AstId<T>, | ||
447 | pub path: path::ModPath, | ||
448 | } | ||
449 | |||
450 | impl<T: ast::AstNode> AstIdWithPath<T> { | ||
451 | pub fn new(file_id: HirFileId, ast_id: FileAstId<T>, path: path::ModPath) -> AstIdWithPath<T> { | ||
452 | AstIdWithPath { ast_id: AstId::new(file_id, ast_id), path } | ||
453 | } | ||
454 | } | ||
455 | |||
456 | impl AsMacroCall for AstIdWithPath<ast::MacroCall> { | ||
457 | fn as_call_id( | ||
458 | &self, | ||
459 | db: &impl AstDatabase, | ||
460 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
461 | ) -> Option<MacroCallId> { | ||
462 | let def = resolver(self.path.clone())?; | ||
463 | Some(def.as_call_id(db, MacroCallKind::FnLike(self.ast_id.clone()))) | ||
464 | } | ||
465 | } | ||
466 | |||
467 | impl AsMacroCall for AstIdWithPath<ast::ModuleItem> { | ||
468 | fn as_call_id( | ||
469 | &self, | ||
470 | db: &impl AstDatabase, | ||
471 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
472 | ) -> Option<MacroCallId> { | ||
473 | let def = resolver(self.path.clone())?; | ||
474 | Some(def.as_call_id(db, MacroCallKind::Attr(self.ast_id.clone()))) | ||
475 | } | ||
476 | } | ||
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 6352c71ef..51c65a5d7 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -7,7 +7,7 @@ use hir_expand::{ | |||
7 | builtin_derive::find_builtin_derive, | 7 | builtin_derive::find_builtin_derive, |
8 | builtin_macro::find_builtin_macro, | 8 | builtin_macro::find_builtin_macro, |
9 | name::{name, AsName, Name}, | 9 | name::{name, AsName, Name}, |
10 | HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | 10 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, |
11 | }; | 11 | }; |
12 | use ra_cfg::CfgOptions; | 12 | use ra_cfg::CfgOptions; |
13 | use ra_db::{CrateId, FileId}; | 13 | use ra_db::{CrateId, FileId}; |
@@ -25,8 +25,9 @@ use crate::{ | |||
25 | path::{ImportAlias, ModPath, PathKind}, | 25 | path::{ImportAlias, ModPath, PathKind}, |
26 | per_ns::PerNs, | 26 | per_ns::PerNs, |
27 | visibility::Visibility, | 27 | visibility::Visibility, |
28 | AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern, | 28 | AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, |
29 | LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, | 29 | FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, |
30 | TraitLoc, TypeAliasLoc, UnionLoc, | ||
30 | }; | 31 | }; |
31 | 32 | ||
32 | pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { | 33 | pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { |
@@ -99,11 +100,16 @@ struct ImportDirective { | |||
99 | #[derive(Clone, Debug, Eq, PartialEq)] | 100 | #[derive(Clone, Debug, Eq, PartialEq)] |
100 | struct MacroDirective { | 101 | struct MacroDirective { |
101 | module_id: LocalModuleId, | 102 | module_id: LocalModuleId, |
102 | ast_id: AstId<ast::MacroCall>, | 103 | ast_id: AstIdWithPath<ast::MacroCall>, |
103 | path: ModPath, | ||
104 | legacy: Option<MacroCallId>, | 104 | legacy: Option<MacroCallId>, |
105 | } | 105 | } |
106 | 106 | ||
107 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
108 | struct DeriveDirective { | ||
109 | module_id: LocalModuleId, | ||
110 | ast_id: AstIdWithPath<ast::ModuleItem>, | ||
111 | } | ||
112 | |||
107 | /// Walks the tree of module recursively | 113 | /// Walks the tree of module recursively |
108 | struct DefCollector<'a, DB> { | 114 | struct DefCollector<'a, DB> { |
109 | db: &'a DB, | 115 | db: &'a DB, |
@@ -112,7 +118,7 @@ struct DefCollector<'a, DB> { | |||
112 | unresolved_imports: Vec<ImportDirective>, | 118 | unresolved_imports: Vec<ImportDirective>, |
113 | resolved_imports: Vec<ImportDirective>, | 119 | resolved_imports: Vec<ImportDirective>, |
114 | unexpanded_macros: Vec<MacroDirective>, | 120 | unexpanded_macros: Vec<MacroDirective>, |
115 | unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, ModPath)>, | 121 | unexpanded_attribute_macros: Vec<DeriveDirective>, |
116 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, | 122 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, |
117 | cfg_options: &'a CfgOptions, | 123 | cfg_options: &'a CfgOptions, |
118 | } | 124 | } |
@@ -146,7 +152,7 @@ where | |||
146 | ReachedFixedPoint::Yes => break, | 152 | ReachedFixedPoint::Yes => break, |
147 | ReachedFixedPoint::No => i += 1, | 153 | ReachedFixedPoint::No => i += 1, |
148 | } | 154 | } |
149 | if i == 1000 { | 155 | if i == 10000 { |
150 | log::error!("name resolution is stuck"); | 156 | log::error!("name resolution is stuck"); |
151 | break; | 157 | break; |
152 | } | 158 | } |
@@ -515,16 +521,16 @@ where | |||
515 | return false; | 521 | return false; |
516 | } | 522 | } |
517 | 523 | ||
518 | let resolved_res = self.def_map.resolve_path_fp_with_macro( | 524 | if let Some(call_id) = directive.ast_id.as_call_id(self.db, |path| { |
519 | self.db, | 525 | let resolved_res = self.def_map.resolve_path_fp_with_macro( |
520 | ResolveMode::Other, | 526 | self.db, |
521 | directive.module_id, | 527 | ResolveMode::Other, |
522 | &directive.path, | 528 | directive.module_id, |
523 | BuiltinShadowMode::Module, | 529 | &path, |
524 | ); | 530 | BuiltinShadowMode::Module, |
525 | 531 | ); | |
526 | if let Some(def) = resolved_res.resolved_def.take_macros() { | 532 | resolved_res.resolved_def.take_macros() |
527 | let call_id = def.as_call_id(self.db, MacroCallKind::FnLike(directive.ast_id)); | 533 | }) { |
528 | resolved.push((directive.module_id, call_id)); | 534 | resolved.push((directive.module_id, call_id)); |
529 | res = ReachedFixedPoint::No; | 535 | res = ReachedFixedPoint::No; |
530 | return false; | 536 | return false; |
@@ -532,12 +538,11 @@ where | |||
532 | 538 | ||
533 | true | 539 | true |
534 | }); | 540 | }); |
535 | attribute_macros.retain(|(module_id, ast_id, path)| { | 541 | attribute_macros.retain(|directive| { |
536 | let resolved_res = self.resolve_attribute_macro(path); | 542 | if let Some(call_id) = |
537 | 543 | directive.ast_id.as_call_id(self.db, |path| self.resolve_attribute_macro(&path)) | |
538 | if let Some(def) = resolved_res { | 544 | { |
539 | let call_id = def.as_call_id(self.db, MacroCallKind::Attr(*ast_id)); | 545 | resolved.push((directive.module_id, call_id)); |
540 | resolved.push((*module_id, call_id)); | ||
541 | res = ReachedFixedPoint::No; | 546 | res = ReachedFixedPoint::No; |
542 | return false; | 547 | return false; |
543 | } | 548 | } |
@@ -833,20 +838,22 @@ where | |||
833 | }; | 838 | }; |
834 | let path = ModPath::from_tt_ident(ident); | 839 | let path = ModPath::from_tt_ident(ident); |
835 | 840 | ||
836 | let ast_id = AstId::new(self.file_id, def.kind.ast_id()); | 841 | let ast_id = AstIdWithPath::new(self.file_id, def.kind.ast_id(), path); |
837 | self.def_collector.unexpanded_attribute_macros.push((self.module_id, ast_id, path)); | 842 | self.def_collector |
843 | .unexpanded_attribute_macros | ||
844 | .push(DeriveDirective { module_id: self.module_id, ast_id }); | ||
838 | } | 845 | } |
839 | } | 846 | } |
840 | } | 847 | } |
841 | 848 | ||
842 | fn collect_macro(&mut self, mac: &raw::MacroData) { | 849 | fn collect_macro(&mut self, mac: &raw::MacroData) { |
843 | let ast_id = AstId::new(self.file_id, mac.ast_id); | 850 | let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); |
844 | 851 | ||
845 | // Case 0: builtin macros | 852 | // Case 0: builtin macros |
846 | if mac.builtin { | 853 | if mac.builtin { |
847 | if let Some(name) = &mac.name { | 854 | if let Some(name) = &mac.name { |
848 | let krate = self.def_collector.def_map.krate; | 855 | let krate = self.def_collector.def_map.krate; |
849 | if let Some(macro_id) = find_builtin_macro(name, krate, ast_id) { | 856 | if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) { |
850 | self.def_collector.define_macro( | 857 | self.def_collector.define_macro( |
851 | self.module_id, | 858 | self.module_id, |
852 | name.clone(), | 859 | name.clone(), |
@@ -862,7 +869,7 @@ where | |||
862 | if is_macro_rules(&mac.path) { | 869 | if is_macro_rules(&mac.path) { |
863 | if let Some(name) = &mac.name { | 870 | if let Some(name) = &mac.name { |
864 | let macro_id = MacroDefId { | 871 | let macro_id = MacroDefId { |
865 | ast_id: Some(ast_id), | 872 | ast_id: Some(ast_id.ast_id), |
866 | krate: Some(self.def_collector.def_map.krate), | 873 | krate: Some(self.def_collector.def_map.krate), |
867 | kind: MacroDefKind::Declarative, | 874 | kind: MacroDefKind::Declarative, |
868 | }; | 875 | }; |
@@ -872,15 +879,13 @@ where | |||
872 | } | 879 | } |
873 | 880 | ||
874 | // Case 2: try to resolve in legacy scope and expand macro_rules | 881 | // Case 2: try to resolve in legacy scope and expand macro_rules |
875 | if let Some(macro_def) = mac.path.as_ident().and_then(|name| { | 882 | if let Some(macro_call_id) = ast_id.as_call_id(self.def_collector.db, |path| { |
876 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) | 883 | path.as_ident().and_then(|name| { |
884 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) | ||
885 | }) | ||
877 | }) { | 886 | }) { |
878 | let macro_call_id = | ||
879 | macro_def.as_call_id(self.def_collector.db, MacroCallKind::FnLike(ast_id)); | ||
880 | |||
881 | self.def_collector.unexpanded_macros.push(MacroDirective { | 887 | self.def_collector.unexpanded_macros.push(MacroDirective { |
882 | module_id: self.module_id, | 888 | module_id: self.module_id, |
883 | path: mac.path.clone(), | ||
884 | ast_id, | 889 | ast_id, |
885 | legacy: Some(macro_call_id), | 890 | legacy: Some(macro_call_id), |
886 | }); | 891 | }); |
@@ -890,14 +895,12 @@ where | |||
890 | 895 | ||
891 | // Case 3: resolve in module scope, expand during name resolution. | 896 | // Case 3: resolve in module scope, expand during name resolution. |
892 | // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only. | 897 | // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only. |
893 | let mut path = mac.path.clone(); | 898 | if ast_id.path.is_ident() { |
894 | if path.is_ident() { | 899 | ast_id.path.kind = PathKind::Super(0); |
895 | path.kind = PathKind::Super(0); | ||
896 | } | 900 | } |
897 | 901 | ||
898 | self.def_collector.unexpanded_macros.push(MacroDirective { | 902 | self.def_collector.unexpanded_macros.push(MacroDirective { |
899 | module_id: self.module_id, | 903 | module_id: self.module_id, |
900 | path, | ||
901 | ast_id, | 904 | ast_id, |
902 | legacy: None, | 905 | legacy: None, |
903 | }); | 906 | }); |
diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs index 05cf4646a..e2b228e80 100644 --- a/crates/ra_hir_def/src/resolver.rs +++ b/crates/ra_hir_def/src/resolver.rs | |||
@@ -542,11 +542,7 @@ impl Resolver { | |||
542 | 542 | ||
543 | fn push_generic_params_scope(self, db: &impl DefDatabase, def: GenericDefId) -> Resolver { | 543 | fn push_generic_params_scope(self, db: &impl DefDatabase, def: GenericDefId) -> Resolver { |
544 | let params = db.generic_params(def); | 544 | let params = db.generic_params(def); |
545 | if params.types.is_empty() { | 545 | self.push_scope(Scope::GenericParams { def, params }) |
546 | self | ||
547 | } else { | ||
548 | self.push_scope(Scope::GenericParams { def, params }) | ||
549 | } | ||
550 | } | 546 | } |
551 | 547 | ||
552 | fn push_impl_block_scope(self, impl_block: ImplId) -> Resolver { | 548 | fn push_impl_block_scope(self, impl_block: ImplId) -> Resolver { |
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 7cf3b59a7..9506f2e1c 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs | |||
@@ -323,11 +323,18 @@ impl<T: Clone> InFile<&T> { | |||
323 | } | 323 | } |
324 | } | 324 | } |
325 | 325 | ||
326 | impl<T> InFile<Option<T>> { | ||
327 | pub fn transpose(self) -> Option<InFile<T>> { | ||
328 | let value = self.value?; | ||
329 | Some(InFile::new(self.file_id, value)) | ||
330 | } | ||
331 | } | ||
332 | |||
326 | impl InFile<SyntaxNode> { | 333 | impl InFile<SyntaxNode> { |
327 | pub fn ancestors_with_macros<'a>( | 334 | pub fn ancestors_with_macros( |
328 | self, | 335 | self, |
329 | db: &'a impl crate::db::AstDatabase, | 336 | db: &impl crate::db::AstDatabase, |
330 | ) -> impl Iterator<Item = InFile<SyntaxNode>> + 'a { | 337 | ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ { |
331 | std::iter::successors(Some(self), move |node| match node.value.parent() { | 338 | std::iter::successors(Some(self), move |node| match node.value.parent() { |
332 | Some(parent) => Some(node.with_value(parent)), | 339 | Some(parent) => Some(node.with_value(parent)), |
333 | None => { | 340 | None => { |
@@ -338,6 +345,15 @@ impl InFile<SyntaxNode> { | |||
338 | } | 345 | } |
339 | } | 346 | } |
340 | 347 | ||
348 | impl InFile<SyntaxToken> { | ||
349 | pub fn ancestors_with_macros( | ||
350 | self, | ||
351 | db: &impl crate::db::AstDatabase, | ||
352 | ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ { | ||
353 | self.map(|it| it.parent()).ancestors_with_macros(db) | ||
354 | } | ||
355 | } | ||
356 | |||
341 | impl<N: AstNode> InFile<N> { | 357 | impl<N: AstNode> InFile<N> { |
342 | pub fn descendants<T: AstNode>(self) -> impl Iterator<Item = InFile<T>> { | 358 | pub fn descendants<T: AstNode>(self) -> impl Iterator<Item = InFile<T>> { |
343 | self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n)) | 359 | self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n)) |
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index d1ff85f0f..14e089cf4 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs | |||
@@ -2,7 +2,12 @@ | |||
2 | 2 | ||
3 | use std::fmt; | 3 | use std::fmt; |
4 | 4 | ||
5 | use crate::db::HirDatabase; | 5 | use crate::{ |
6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, | ||
7 | Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, | ||
8 | }; | ||
9 | use hir_def::{generics::TypeParamProvenance, AdtId, AssocContainerId, Lookup}; | ||
10 | use hir_expand::name::Name; | ||
6 | 11 | ||
7 | pub struct HirFormatter<'a, 'b, DB> { | 12 | pub struct HirFormatter<'a, 'b, DB> { |
8 | pub db: &'a DB, | 13 | pub db: &'a DB, |
@@ -97,3 +102,369 @@ where | |||
97 | }) | 102 | }) |
98 | } | 103 | } |
99 | } | 104 | } |
105 | |||
106 | const TYPE_HINT_TRUNCATION: &str = "…"; | ||
107 | |||
108 | impl HirDisplay for &Ty { | ||
109 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
110 | HirDisplay::hir_fmt(*self, f) | ||
111 | } | ||
112 | } | ||
113 | |||
114 | impl HirDisplay for ApplicationTy { | ||
115 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
116 | if f.should_truncate() { | ||
117 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
118 | } | ||
119 | |||
120 | match self.ctor { | ||
121 | TypeCtor::Bool => write!(f, "bool")?, | ||
122 | TypeCtor::Char => write!(f, "char")?, | ||
123 | TypeCtor::Int(t) => write!(f, "{}", t)?, | ||
124 | TypeCtor::Float(t) => write!(f, "{}", t)?, | ||
125 | TypeCtor::Str => write!(f, "str")?, | ||
126 | TypeCtor::Slice => { | ||
127 | let t = self.parameters.as_single(); | ||
128 | write!(f, "[{}]", t.display(f.db))?; | ||
129 | } | ||
130 | TypeCtor::Array => { | ||
131 | let t = self.parameters.as_single(); | ||
132 | write!(f, "[{}; _]", t.display(f.db))?; | ||
133 | } | ||
134 | TypeCtor::RawPtr(m) => { | ||
135 | let t = self.parameters.as_single(); | ||
136 | write!(f, "*{}{}", m.as_keyword_for_ptr(), t.display(f.db))?; | ||
137 | } | ||
138 | TypeCtor::Ref(m) => { | ||
139 | let t = self.parameters.as_single(); | ||
140 | let ty_display = if f.omit_verbose_types() { | ||
141 | t.display_truncated(f.db, f.max_size) | ||
142 | } else { | ||
143 | t.display(f.db) | ||
144 | }; | ||
145 | write!(f, "&{}{}", m.as_keyword_for_ref(), ty_display)?; | ||
146 | } | ||
147 | TypeCtor::Never => write!(f, "!")?, | ||
148 | TypeCtor::Tuple { .. } => { | ||
149 | let ts = &self.parameters; | ||
150 | if ts.len() == 1 { | ||
151 | write!(f, "({},)", ts[0].display(f.db))?; | ||
152 | } else { | ||
153 | write!(f, "(")?; | ||
154 | f.write_joined(&*ts.0, ", ")?; | ||
155 | write!(f, ")")?; | ||
156 | } | ||
157 | } | ||
158 | TypeCtor::FnPtr { .. } => { | ||
159 | let sig = FnSig::from_fn_ptr_substs(&self.parameters); | ||
160 | write!(f, "fn(")?; | ||
161 | f.write_joined(sig.params(), ", ")?; | ||
162 | write!(f, ") -> {}", sig.ret().display(f.db))?; | ||
163 | } | ||
164 | TypeCtor::FnDef(def) => { | ||
165 | let sig = f.db.callable_item_signature(def).subst(&self.parameters); | ||
166 | let name = match def { | ||
167 | CallableDef::FunctionId(ff) => f.db.function_data(ff).name.clone(), | ||
168 | CallableDef::StructId(s) => f.db.struct_data(s).name.clone(), | ||
169 | CallableDef::EnumVariantId(e) => { | ||
170 | let enum_data = f.db.enum_data(e.parent); | ||
171 | enum_data.variants[e.local_id].name.clone() | ||
172 | } | ||
173 | }; | ||
174 | match def { | ||
175 | CallableDef::FunctionId(_) => write!(f, "fn {}", name)?, | ||
176 | CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => { | ||
177 | write!(f, "{}", name)? | ||
178 | } | ||
179 | } | ||
180 | if self.parameters.len() > 0 { | ||
181 | let generics = generics(f.db, def.into()); | ||
182 | let (parent_params, self_param, type_params, _impl_trait_params) = | ||
183 | generics.provenance_split(); | ||
184 | let total_len = parent_params + self_param + type_params; | ||
185 | // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? | ||
186 | if total_len > 0 { | ||
187 | write!(f, "<")?; | ||
188 | f.write_joined(&self.parameters.0[..total_len], ", ")?; | ||
189 | write!(f, ">")?; | ||
190 | } | ||
191 | } | ||
192 | write!(f, "(")?; | ||
193 | f.write_joined(sig.params(), ", ")?; | ||
194 | write!(f, ") -> {}", sig.ret().display(f.db))?; | ||
195 | } | ||
196 | TypeCtor::Adt(def_id) => { | ||
197 | let name = match def_id { | ||
198 | AdtId::StructId(it) => f.db.struct_data(it).name.clone(), | ||
199 | AdtId::UnionId(it) => f.db.union_data(it).name.clone(), | ||
200 | AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), | ||
201 | }; | ||
202 | write!(f, "{}", name)?; | ||
203 | if self.parameters.len() > 0 { | ||
204 | write!(f, "<")?; | ||
205 | |||
206 | let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); | ||
207 | let parameters_to_write = if f.omit_verbose_types() { | ||
208 | match self | ||
209 | .ctor | ||
210 | .as_generic_def() | ||
211 | .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) | ||
212 | .filter(|defaults| !defaults.is_empty()) | ||
213 | { | ||
214 | Option::None => self.parameters.0.as_ref(), | ||
215 | Option::Some(default_parameters) => { | ||
216 | for (i, parameter) in self.parameters.iter().enumerate() { | ||
217 | match (parameter, default_parameters.get(i)) { | ||
218 | (&Ty::Unknown, _) | (_, None) => { | ||
219 | non_default_parameters.push(parameter.clone()) | ||
220 | } | ||
221 | (_, Some(default_parameter)) | ||
222 | if parameter != default_parameter => | ||
223 | { | ||
224 | non_default_parameters.push(parameter.clone()) | ||
225 | } | ||
226 | _ => (), | ||
227 | } | ||
228 | } | ||
229 | &non_default_parameters | ||
230 | } | ||
231 | } | ||
232 | } else { | ||
233 | self.parameters.0.as_ref() | ||
234 | }; | ||
235 | |||
236 | f.write_joined(parameters_to_write, ", ")?; | ||
237 | write!(f, ">")?; | ||
238 | } | ||
239 | } | ||
240 | TypeCtor::AssociatedType(type_alias) => { | ||
241 | let trait_ = match type_alias.lookup(f.db).container { | ||
242 | AssocContainerId::TraitId(it) => it, | ||
243 | _ => panic!("not an associated type"), | ||
244 | }; | ||
245 | let trait_name = f.db.trait_data(trait_).name.clone(); | ||
246 | let name = f.db.type_alias_data(type_alias).name.clone(); | ||
247 | write!(f, "{}::{}", trait_name, name)?; | ||
248 | if self.parameters.len() > 0 { | ||
249 | write!(f, "<")?; | ||
250 | f.write_joined(&*self.parameters.0, ", ")?; | ||
251 | write!(f, ">")?; | ||
252 | } | ||
253 | } | ||
254 | TypeCtor::Closure { .. } => { | ||
255 | let sig = self.parameters[0] | ||
256 | .callable_sig(f.db) | ||
257 | .expect("first closure parameter should contain signature"); | ||
258 | let return_type_hint = sig.ret().display(f.db); | ||
259 | if sig.params().is_empty() { | ||
260 | write!(f, "|| -> {}", return_type_hint)?; | ||
261 | } else if f.omit_verbose_types() { | ||
262 | write!(f, "|{}| -> {}", TYPE_HINT_TRUNCATION, return_type_hint)?; | ||
263 | } else { | ||
264 | write!(f, "|")?; | ||
265 | f.write_joined(sig.params(), ", ")?; | ||
266 | write!(f, "| -> {}", return_type_hint)?; | ||
267 | }; | ||
268 | } | ||
269 | } | ||
270 | Ok(()) | ||
271 | } | ||
272 | } | ||
273 | |||
274 | impl HirDisplay for ProjectionTy { | ||
275 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
276 | if f.should_truncate() { | ||
277 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
278 | } | ||
279 | |||
280 | let trait_name = f.db.trait_data(self.trait_(f.db)).name.clone(); | ||
281 | write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?; | ||
282 | if self.parameters.len() > 1 { | ||
283 | write!(f, "<")?; | ||
284 | f.write_joined(&self.parameters[1..], ", ")?; | ||
285 | write!(f, ">")?; | ||
286 | } | ||
287 | write!(f, ">::{}", f.db.type_alias_data(self.associated_ty).name)?; | ||
288 | Ok(()) | ||
289 | } | ||
290 | } | ||
291 | |||
292 | impl HirDisplay for Ty { | ||
293 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
294 | if f.should_truncate() { | ||
295 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
296 | } | ||
297 | |||
298 | match self { | ||
299 | Ty::Apply(a_ty) => a_ty.hir_fmt(f)?, | ||
300 | Ty::Projection(p_ty) => p_ty.hir_fmt(f)?, | ||
301 | Ty::Placeholder(id) => { | ||
302 | let generics = generics(f.db, id.parent); | ||
303 | let param_data = &generics.params.types[id.local_id]; | ||
304 | match param_data.provenance { | ||
305 | TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { | ||
306 | write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))? | ||
307 | } | ||
308 | TypeParamProvenance::ArgumentImplTrait => { | ||
309 | write!(f, "impl ")?; | ||
310 | let bounds = f.db.generic_predicates_for_param(*id); | ||
311 | let substs = Substs::type_params_for_generics(&generics); | ||
312 | write_bounds_like_dyn_trait( | ||
313 | &bounds.iter().map(|b| b.clone().subst(&substs)).collect::<Vec<_>>(), | ||
314 | f, | ||
315 | )?; | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | Ty::Bound(idx) => write!(f, "?{}", idx)?, | ||
320 | Ty::Dyn(predicates) | Ty::Opaque(predicates) => { | ||
321 | match self { | ||
322 | Ty::Dyn(_) => write!(f, "dyn ")?, | ||
323 | Ty::Opaque(_) => write!(f, "impl ")?, | ||
324 | _ => unreachable!(), | ||
325 | }; | ||
326 | write_bounds_like_dyn_trait(&predicates, f)?; | ||
327 | } | ||
328 | Ty::Unknown => write!(f, "{{unknown}}")?, | ||
329 | Ty::Infer(..) => write!(f, "_")?, | ||
330 | } | ||
331 | Ok(()) | ||
332 | } | ||
333 | } | ||
334 | |||
335 | fn write_bounds_like_dyn_trait( | ||
336 | predicates: &[GenericPredicate], | ||
337 | f: &mut HirFormatter<impl HirDatabase>, | ||
338 | ) -> fmt::Result { | ||
339 | // Note: This code is written to produce nice results (i.e. | ||
340 | // corresponding to surface Rust) for types that can occur in | ||
341 | // actual Rust. It will have weird results if the predicates | ||
342 | // aren't as expected (i.e. self types = $0, projection | ||
343 | // predicates for a certain trait come after the Implemented | ||
344 | // predicate for that trait). | ||
345 | let mut first = true; | ||
346 | let mut angle_open = false; | ||
347 | for p in predicates.iter() { | ||
348 | match p { | ||
349 | GenericPredicate::Implemented(trait_ref) => { | ||
350 | if angle_open { | ||
351 | write!(f, ">")?; | ||
352 | } | ||
353 | if !first { | ||
354 | write!(f, " + ")?; | ||
355 | } | ||
356 | // We assume that the self type is $0 (i.e. the | ||
357 | // existential) here, which is the only thing that's | ||
358 | // possible in actual Rust, and hence don't print it | ||
359 | write!(f, "{}", f.db.trait_data(trait_ref.trait_).name.clone())?; | ||
360 | if trait_ref.substs.len() > 1 { | ||
361 | write!(f, "<")?; | ||
362 | f.write_joined(&trait_ref.substs[1..], ", ")?; | ||
363 | // there might be assoc type bindings, so we leave the angle brackets open | ||
364 | angle_open = true; | ||
365 | } | ||
366 | } | ||
367 | GenericPredicate::Projection(projection_pred) => { | ||
368 | // in types in actual Rust, these will always come | ||
369 | // after the corresponding Implemented predicate | ||
370 | if angle_open { | ||
371 | write!(f, ", ")?; | ||
372 | } else { | ||
373 | write!(f, "<")?; | ||
374 | angle_open = true; | ||
375 | } | ||
376 | let name = | ||
377 | f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name.clone(); | ||
378 | write!(f, "{} = ", name)?; | ||
379 | projection_pred.ty.hir_fmt(f)?; | ||
380 | } | ||
381 | GenericPredicate::Error => { | ||
382 | if angle_open { | ||
383 | // impl Trait<X, {error}> | ||
384 | write!(f, ", ")?; | ||
385 | } else if !first { | ||
386 | // impl Trait + {error} | ||
387 | write!(f, " + ")?; | ||
388 | } | ||
389 | p.hir_fmt(f)?; | ||
390 | } | ||
391 | } | ||
392 | first = false; | ||
393 | } | ||
394 | if angle_open { | ||
395 | write!(f, ">")?; | ||
396 | } | ||
397 | Ok(()) | ||
398 | } | ||
399 | |||
400 | impl TraitRef { | ||
401 | fn hir_fmt_ext(&self, f: &mut HirFormatter<impl HirDatabase>, use_as: bool) -> fmt::Result { | ||
402 | if f.should_truncate() { | ||
403 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
404 | } | ||
405 | |||
406 | self.substs[0].hir_fmt(f)?; | ||
407 | if use_as { | ||
408 | write!(f, " as ")?; | ||
409 | } else { | ||
410 | write!(f, ": ")?; | ||
411 | } | ||
412 | write!(f, "{}", f.db.trait_data(self.trait_).name.clone())?; | ||
413 | if self.substs.len() > 1 { | ||
414 | write!(f, "<")?; | ||
415 | f.write_joined(&self.substs[1..], ", ")?; | ||
416 | write!(f, ">")?; | ||
417 | } | ||
418 | Ok(()) | ||
419 | } | ||
420 | } | ||
421 | |||
422 | impl HirDisplay for TraitRef { | ||
423 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
424 | self.hir_fmt_ext(f, false) | ||
425 | } | ||
426 | } | ||
427 | |||
428 | impl HirDisplay for &GenericPredicate { | ||
429 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
430 | HirDisplay::hir_fmt(*self, f) | ||
431 | } | ||
432 | } | ||
433 | |||
434 | impl HirDisplay for GenericPredicate { | ||
435 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
436 | if f.should_truncate() { | ||
437 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
438 | } | ||
439 | |||
440 | match self { | ||
441 | GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, | ||
442 | GenericPredicate::Projection(projection_pred) => { | ||
443 | write!(f, "<")?; | ||
444 | projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?; | ||
445 | write!( | ||
446 | f, | ||
447 | ">::{} = {}", | ||
448 | f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name, | ||
449 | projection_pred.ty.display(f.db) | ||
450 | )?; | ||
451 | } | ||
452 | GenericPredicate::Error => write!(f, "{{error}}")?, | ||
453 | } | ||
454 | Ok(()) | ||
455 | } | ||
456 | } | ||
457 | |||
458 | impl HirDisplay for Obligation { | ||
459 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
460 | match self { | ||
461 | Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), | ||
462 | Obligation::Projection(proj) => write!( | ||
463 | f, | ||
464 | "Normalize({} => {})", | ||
465 | proj.projection_ty.display(f.db), | ||
466 | proj.ty.display(f.db) | ||
467 | ), | ||
468 | } | ||
469 | } | ||
470 | } | ||
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 3c9c02d03..39d8bc0ca 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs | |||
@@ -165,12 +165,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
165 | Expr::Match { expr, arms } => { | 165 | Expr::Match { expr, arms } => { |
166 | let input_ty = self.infer_expr(*expr, &Expectation::none()); | 166 | let input_ty = self.infer_expr(*expr, &Expectation::none()); |
167 | 167 | ||
168 | let mut result_ty = self.table.new_maybe_never_type_var(); | 168 | let mut result_ty = if arms.len() == 0 { |
169 | Ty::simple(TypeCtor::Never) | ||
170 | } else { | ||
171 | self.table.new_type_var() | ||
172 | }; | ||
169 | 173 | ||
170 | for arm in arms { | 174 | for arm in arms { |
171 | for &pat in &arm.pats { | 175 | let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); |
172 | let _pat_ty = self.infer_pat(pat, &input_ty, BindingMode::default()); | ||
173 | } | ||
174 | if let Some(guard_expr) = arm.guard { | 176 | if let Some(guard_expr) = arm.guard { |
175 | self.infer_expr( | 177 | self.infer_expr( |
176 | guard_expr, | 178 | guard_expr, |
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs index e7283f24c..a5dfdf6c4 100644 --- a/crates/ra_hir_ty/src/infer/pat.rs +++ b/crates/ra_hir_ty/src/infer/pat.rs | |||
@@ -82,6 +82,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
82 | 82 | ||
83 | let is_non_ref_pat = match &body[pat] { | 83 | let is_non_ref_pat = match &body[pat] { |
84 | Pat::Tuple(..) | 84 | Pat::Tuple(..) |
85 | | Pat::Or(..) | ||
85 | | Pat::TupleStruct { .. } | 86 | | Pat::TupleStruct { .. } |
86 | | Pat::Record { .. } | 87 | | Pat::Record { .. } |
87 | | Pat::Range { .. } | 88 | | Pat::Range { .. } |
@@ -126,6 +127,17 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
126 | 127 | ||
127 | Ty::apply(TypeCtor::Tuple { cardinality: args.len() as u16 }, Substs(inner_tys)) | 128 | Ty::apply(TypeCtor::Tuple { cardinality: args.len() as u16 }, Substs(inner_tys)) |
128 | } | 129 | } |
130 | Pat::Or(ref pats) => { | ||
131 | if let Some((first_pat, rest)) = pats.split_first() { | ||
132 | let ty = self.infer_pat(*first_pat, expected, default_bm); | ||
133 | for pat in rest { | ||
134 | self.infer_pat(*pat, expected, default_bm); | ||
135 | } | ||
136 | ty | ||
137 | } else { | ||
138 | Ty::Unknown | ||
139 | } | ||
140 | } | ||
129 | Pat::Ref { pat, mutability } => { | 141 | Pat::Ref { pat, mutability } => { |
130 | let expectation = match expected.as_reference() { | 142 | let expectation = match expected.as_reference() { |
131 | Some((inner_ty, exp_mut)) => { | 143 | Some((inner_ty, exp_mut)) => { |
diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index fe05642ae..1dc842f40 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs | |||
@@ -249,6 +249,8 @@ impl InferenceTable { | |||
249 | match (ty1, ty2) { | 249 | match (ty1, ty2) { |
250 | (Ty::Unknown, _) | (_, Ty::Unknown) => true, | 250 | (Ty::Unknown, _) | (_, Ty::Unknown) => true, |
251 | 251 | ||
252 | (Ty::Placeholder(p1), Ty::Placeholder(p2)) if *p1 == *p2 => true, | ||
253 | |||
252 | (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) | 254 | (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) |
253 | | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2))) | 255 | | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2))) |
254 | | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) | 256 | | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index c5fe18c85..571579cc4 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -41,13 +41,12 @@ mod marks; | |||
41 | 41 | ||
42 | use std::ops::Deref; | 42 | use std::ops::Deref; |
43 | use std::sync::Arc; | 43 | use std::sync::Arc; |
44 | use std::{fmt, iter, mem}; | 44 | use std::{iter, mem}; |
45 | 45 | ||
46 | use hir_def::{ | 46 | use hir_def::{ |
47 | expr::ExprId, generics::TypeParamProvenance, type_ref::Mutability, AdtId, AssocContainerId, | 47 | expr::ExprId, type_ref::Mutability, AdtId, AssocContainerId, DefWithBodyId, GenericDefId, |
48 | DefWithBodyId, GenericDefId, HasModule, Lookup, TraitId, TypeAliasId, TypeParamId, | 48 | HasModule, Lookup, TraitId, TypeAliasId, TypeParamId, |
49 | }; | 49 | }; |
50 | use hir_expand::name::Name; | ||
51 | use ra_db::{impl_intern_key, salsa, CrateId}; | 50 | use ra_db::{impl_intern_key, salsa, CrateId}; |
52 | 51 | ||
53 | use crate::{ | 52 | use crate::{ |
@@ -55,7 +54,7 @@ use crate::{ | |||
55 | primitive::{FloatTy, IntTy, Uncertain}, | 54 | primitive::{FloatTy, IntTy, Uncertain}, |
56 | utils::{generics, make_mut_slice, Generics}, | 55 | utils::{generics, make_mut_slice, Generics}, |
57 | }; | 56 | }; |
58 | use display::{HirDisplay, HirFormatter}; | 57 | use display::HirDisplay; |
59 | 58 | ||
60 | pub use autoderef::autoderef; | 59 | pub use autoderef::autoderef; |
61 | pub use infer::{do_infer_query, InferTy, InferenceResult}; | 60 | pub use infer::{do_infer_query, InferTy, InferenceResult}; |
@@ -291,7 +290,7 @@ pub enum Ty { | |||
291 | /// {}` when we're type-checking the body of that function. In this | 290 | /// {}` when we're type-checking the body of that function. In this |
292 | /// situation, we know this stands for *some* type, but don't know the exact | 291 | /// situation, we know this stands for *some* type, but don't know the exact |
293 | /// type. | 292 | /// type. |
294 | Param(TypeParamId), | 293 | Placeholder(TypeParamId), |
295 | 294 | ||
296 | /// A bound type variable. This is used in various places: when representing | 295 | /// A bound type variable. This is used in various places: when representing |
297 | /// some polymorphic type like the type of function `fn f<T>`, the type | 296 | /// some polymorphic type like the type of function `fn f<T>`, the type |
@@ -365,7 +364,7 @@ impl Substs { | |||
365 | 364 | ||
366 | /// Return Substs that replace each parameter by itself (i.e. `Ty::Param`). | 365 | /// Return Substs that replace each parameter by itself (i.e. `Ty::Param`). |
367 | pub(crate) fn type_params_for_generics(generic_params: &Generics) -> Substs { | 366 | pub(crate) fn type_params_for_generics(generic_params: &Generics) -> Substs { |
368 | Substs(generic_params.iter().map(|(id, _)| Ty::Param(id)).collect()) | 367 | Substs(generic_params.iter().map(|(id, _)| Ty::Placeholder(id)).collect()) |
369 | } | 368 | } |
370 | 369 | ||
371 | /// Return Substs that replace each parameter by itself (i.e. `Ty::Param`). | 370 | /// Return Substs that replace each parameter by itself (i.e. `Ty::Param`). |
@@ -813,7 +812,7 @@ impl TypeWalk for Ty { | |||
813 | p.walk(f); | 812 | p.walk(f); |
814 | } | 813 | } |
815 | } | 814 | } |
816 | Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} | 815 | Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} |
817 | } | 816 | } |
818 | f(self); | 817 | f(self); |
819 | } | 818 | } |
@@ -831,374 +830,8 @@ impl TypeWalk for Ty { | |||
831 | p.walk_mut_binders(f, binders + 1); | 830 | p.walk_mut_binders(f, binders + 1); |
832 | } | 831 | } |
833 | } | 832 | } |
834 | Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} | 833 | Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} |
835 | } | 834 | } |
836 | f(self, binders); | 835 | f(self, binders); |
837 | } | 836 | } |
838 | } | 837 | } |
839 | |||
840 | const TYPE_HINT_TRUNCATION: &str = "…"; | ||
841 | |||
842 | impl HirDisplay for &Ty { | ||
843 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
844 | HirDisplay::hir_fmt(*self, f) | ||
845 | } | ||
846 | } | ||
847 | |||
848 | impl HirDisplay for ApplicationTy { | ||
849 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
850 | if f.should_truncate() { | ||
851 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
852 | } | ||
853 | |||
854 | match self.ctor { | ||
855 | TypeCtor::Bool => write!(f, "bool")?, | ||
856 | TypeCtor::Char => write!(f, "char")?, | ||
857 | TypeCtor::Int(t) => write!(f, "{}", t)?, | ||
858 | TypeCtor::Float(t) => write!(f, "{}", t)?, | ||
859 | TypeCtor::Str => write!(f, "str")?, | ||
860 | TypeCtor::Slice => { | ||
861 | let t = self.parameters.as_single(); | ||
862 | write!(f, "[{}]", t.display(f.db))?; | ||
863 | } | ||
864 | TypeCtor::Array => { | ||
865 | let t = self.parameters.as_single(); | ||
866 | write!(f, "[{}; _]", t.display(f.db))?; | ||
867 | } | ||
868 | TypeCtor::RawPtr(m) => { | ||
869 | let t = self.parameters.as_single(); | ||
870 | write!(f, "*{}{}", m.as_keyword_for_ptr(), t.display(f.db))?; | ||
871 | } | ||
872 | TypeCtor::Ref(m) => { | ||
873 | let t = self.parameters.as_single(); | ||
874 | let ty_display = if f.omit_verbose_types() { | ||
875 | t.display_truncated(f.db, f.max_size) | ||
876 | } else { | ||
877 | t.display(f.db) | ||
878 | }; | ||
879 | write!(f, "&{}{}", m.as_keyword_for_ref(), ty_display)?; | ||
880 | } | ||
881 | TypeCtor::Never => write!(f, "!")?, | ||
882 | TypeCtor::Tuple { .. } => { | ||
883 | let ts = &self.parameters; | ||
884 | if ts.len() == 1 { | ||
885 | write!(f, "({},)", ts[0].display(f.db))?; | ||
886 | } else { | ||
887 | write!(f, "(")?; | ||
888 | f.write_joined(&*ts.0, ", ")?; | ||
889 | write!(f, ")")?; | ||
890 | } | ||
891 | } | ||
892 | TypeCtor::FnPtr { .. } => { | ||
893 | let sig = FnSig::from_fn_ptr_substs(&self.parameters); | ||
894 | write!(f, "fn(")?; | ||
895 | f.write_joined(sig.params(), ", ")?; | ||
896 | write!(f, ") -> {}", sig.ret().display(f.db))?; | ||
897 | } | ||
898 | TypeCtor::FnDef(def) => { | ||
899 | let sig = f.db.callable_item_signature(def).subst(&self.parameters); | ||
900 | let name = match def { | ||
901 | CallableDef::FunctionId(ff) => f.db.function_data(ff).name.clone(), | ||
902 | CallableDef::StructId(s) => f.db.struct_data(s).name.clone(), | ||
903 | CallableDef::EnumVariantId(e) => { | ||
904 | let enum_data = f.db.enum_data(e.parent); | ||
905 | enum_data.variants[e.local_id].name.clone() | ||
906 | } | ||
907 | }; | ||
908 | match def { | ||
909 | CallableDef::FunctionId(_) => write!(f, "fn {}", name)?, | ||
910 | CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => { | ||
911 | write!(f, "{}", name)? | ||
912 | } | ||
913 | } | ||
914 | if self.parameters.len() > 0 { | ||
915 | let generics = generics(f.db, def.into()); | ||
916 | let (parent_params, self_param, type_params, _impl_trait_params) = | ||
917 | generics.provenance_split(); | ||
918 | let total_len = parent_params + self_param + type_params; | ||
919 | // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? | ||
920 | if total_len > 0 { | ||
921 | write!(f, "<")?; | ||
922 | f.write_joined(&self.parameters.0[..total_len], ", ")?; | ||
923 | write!(f, ">")?; | ||
924 | } | ||
925 | } | ||
926 | write!(f, "(")?; | ||
927 | f.write_joined(sig.params(), ", ")?; | ||
928 | write!(f, ") -> {}", sig.ret().display(f.db))?; | ||
929 | } | ||
930 | TypeCtor::Adt(def_id) => { | ||
931 | let name = match def_id { | ||
932 | AdtId::StructId(it) => f.db.struct_data(it).name.clone(), | ||
933 | AdtId::UnionId(it) => f.db.union_data(it).name.clone(), | ||
934 | AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), | ||
935 | }; | ||
936 | write!(f, "{}", name)?; | ||
937 | if self.parameters.len() > 0 { | ||
938 | write!(f, "<")?; | ||
939 | |||
940 | let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); | ||
941 | let parameters_to_write = if f.omit_verbose_types() { | ||
942 | match self | ||
943 | .ctor | ||
944 | .as_generic_def() | ||
945 | .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) | ||
946 | .filter(|defaults| !defaults.is_empty()) | ||
947 | { | ||
948 | Option::None => self.parameters.0.as_ref(), | ||
949 | Option::Some(default_parameters) => { | ||
950 | for (i, parameter) in self.parameters.iter().enumerate() { | ||
951 | match (parameter, default_parameters.get(i)) { | ||
952 | (&Ty::Unknown, _) | (_, None) => { | ||
953 | non_default_parameters.push(parameter.clone()) | ||
954 | } | ||
955 | (_, Some(default_parameter)) | ||
956 | if parameter != default_parameter => | ||
957 | { | ||
958 | non_default_parameters.push(parameter.clone()) | ||
959 | } | ||
960 | _ => (), | ||
961 | } | ||
962 | } | ||
963 | &non_default_parameters | ||
964 | } | ||
965 | } | ||
966 | } else { | ||
967 | self.parameters.0.as_ref() | ||
968 | }; | ||
969 | |||
970 | f.write_joined(parameters_to_write, ", ")?; | ||
971 | write!(f, ">")?; | ||
972 | } | ||
973 | } | ||
974 | TypeCtor::AssociatedType(type_alias) => { | ||
975 | let trait_ = match type_alias.lookup(f.db).container { | ||
976 | AssocContainerId::TraitId(it) => it, | ||
977 | _ => panic!("not an associated type"), | ||
978 | }; | ||
979 | let trait_name = f.db.trait_data(trait_).name.clone(); | ||
980 | let name = f.db.type_alias_data(type_alias).name.clone(); | ||
981 | write!(f, "{}::{}", trait_name, name)?; | ||
982 | if self.parameters.len() > 0 { | ||
983 | write!(f, "<")?; | ||
984 | f.write_joined(&*self.parameters.0, ", ")?; | ||
985 | write!(f, ">")?; | ||
986 | } | ||
987 | } | ||
988 | TypeCtor::Closure { .. } => { | ||
989 | let sig = self.parameters[0] | ||
990 | .callable_sig(f.db) | ||
991 | .expect("first closure parameter should contain signature"); | ||
992 | let return_type_hint = sig.ret().display(f.db); | ||
993 | if sig.params().is_empty() { | ||
994 | write!(f, "|| -> {}", return_type_hint)?; | ||
995 | } else if f.omit_verbose_types() { | ||
996 | write!(f, "|{}| -> {}", TYPE_HINT_TRUNCATION, return_type_hint)?; | ||
997 | } else { | ||
998 | write!(f, "|")?; | ||
999 | f.write_joined(sig.params(), ", ")?; | ||
1000 | write!(f, "| -> {}", return_type_hint)?; | ||
1001 | }; | ||
1002 | } | ||
1003 | } | ||
1004 | Ok(()) | ||
1005 | } | ||
1006 | } | ||
1007 | |||
1008 | impl HirDisplay for ProjectionTy { | ||
1009 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
1010 | if f.should_truncate() { | ||
1011 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
1012 | } | ||
1013 | |||
1014 | let trait_name = f.db.trait_data(self.trait_(f.db)).name.clone(); | ||
1015 | write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?; | ||
1016 | if self.parameters.len() > 1 { | ||
1017 | write!(f, "<")?; | ||
1018 | f.write_joined(&self.parameters[1..], ", ")?; | ||
1019 | write!(f, ">")?; | ||
1020 | } | ||
1021 | write!(f, ">::{}", f.db.type_alias_data(self.associated_ty).name)?; | ||
1022 | Ok(()) | ||
1023 | } | ||
1024 | } | ||
1025 | |||
1026 | impl HirDisplay for Ty { | ||
1027 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
1028 | if f.should_truncate() { | ||
1029 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
1030 | } | ||
1031 | |||
1032 | match self { | ||
1033 | Ty::Apply(a_ty) => a_ty.hir_fmt(f)?, | ||
1034 | Ty::Projection(p_ty) => p_ty.hir_fmt(f)?, | ||
1035 | Ty::Param(id) => { | ||
1036 | let generics = generics(f.db, id.parent); | ||
1037 | let param_data = &generics.params.types[id.local_id]; | ||
1038 | match param_data.provenance { | ||
1039 | TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { | ||
1040 | write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))? | ||
1041 | } | ||
1042 | TypeParamProvenance::ArgumentImplTrait => { | ||
1043 | write!(f, "impl ")?; | ||
1044 | let bounds = f.db.generic_predicates_for_param(*id); | ||
1045 | let substs = Substs::type_params_for_generics(&generics); | ||
1046 | write_bounds_like_dyn_trait( | ||
1047 | &bounds.iter().map(|b| b.clone().subst(&substs)).collect::<Vec<_>>(), | ||
1048 | f, | ||
1049 | )?; | ||
1050 | } | ||
1051 | } | ||
1052 | } | ||
1053 | Ty::Bound(idx) => write!(f, "?{}", idx)?, | ||
1054 | Ty::Dyn(predicates) | Ty::Opaque(predicates) => { | ||
1055 | match self { | ||
1056 | Ty::Dyn(_) => write!(f, "dyn ")?, | ||
1057 | Ty::Opaque(_) => write!(f, "impl ")?, | ||
1058 | _ => unreachable!(), | ||
1059 | }; | ||
1060 | write_bounds_like_dyn_trait(&predicates, f)?; | ||
1061 | } | ||
1062 | Ty::Unknown => write!(f, "{{unknown}}")?, | ||
1063 | Ty::Infer(..) => write!(f, "_")?, | ||
1064 | } | ||
1065 | Ok(()) | ||
1066 | } | ||
1067 | } | ||
1068 | |||
1069 | fn write_bounds_like_dyn_trait( | ||
1070 | predicates: &[GenericPredicate], | ||
1071 | f: &mut HirFormatter<impl HirDatabase>, | ||
1072 | ) -> fmt::Result { | ||
1073 | // Note: This code is written to produce nice results (i.e. | ||
1074 | // corresponding to surface Rust) for types that can occur in | ||
1075 | // actual Rust. It will have weird results if the predicates | ||
1076 | // aren't as expected (i.e. self types = $0, projection | ||
1077 | // predicates for a certain trait come after the Implemented | ||
1078 | // predicate for that trait). | ||
1079 | let mut first = true; | ||
1080 | let mut angle_open = false; | ||
1081 | for p in predicates.iter() { | ||
1082 | match p { | ||
1083 | GenericPredicate::Implemented(trait_ref) => { | ||
1084 | if angle_open { | ||
1085 | write!(f, ">")?; | ||
1086 | } | ||
1087 | if !first { | ||
1088 | write!(f, " + ")?; | ||
1089 | } | ||
1090 | // We assume that the self type is $0 (i.e. the | ||
1091 | // existential) here, which is the only thing that's | ||
1092 | // possible in actual Rust, and hence don't print it | ||
1093 | write!(f, "{}", f.db.trait_data(trait_ref.trait_).name.clone())?; | ||
1094 | if trait_ref.substs.len() > 1 { | ||
1095 | write!(f, "<")?; | ||
1096 | f.write_joined(&trait_ref.substs[1..], ", ")?; | ||
1097 | // there might be assoc type bindings, so we leave the angle brackets open | ||
1098 | angle_open = true; | ||
1099 | } | ||
1100 | } | ||
1101 | GenericPredicate::Projection(projection_pred) => { | ||
1102 | // in types in actual Rust, these will always come | ||
1103 | // after the corresponding Implemented predicate | ||
1104 | if angle_open { | ||
1105 | write!(f, ", ")?; | ||
1106 | } else { | ||
1107 | write!(f, "<")?; | ||
1108 | angle_open = true; | ||
1109 | } | ||
1110 | let name = | ||
1111 | f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name.clone(); | ||
1112 | write!(f, "{} = ", name)?; | ||
1113 | projection_pred.ty.hir_fmt(f)?; | ||
1114 | } | ||
1115 | GenericPredicate::Error => { | ||
1116 | if angle_open { | ||
1117 | // impl Trait<X, {error}> | ||
1118 | write!(f, ", ")?; | ||
1119 | } else if !first { | ||
1120 | // impl Trait + {error} | ||
1121 | write!(f, " + ")?; | ||
1122 | } | ||
1123 | p.hir_fmt(f)?; | ||
1124 | } | ||
1125 | } | ||
1126 | first = false; | ||
1127 | } | ||
1128 | if angle_open { | ||
1129 | write!(f, ">")?; | ||
1130 | } | ||
1131 | Ok(()) | ||
1132 | } | ||
1133 | |||
1134 | impl TraitRef { | ||
1135 | fn hir_fmt_ext(&self, f: &mut HirFormatter<impl HirDatabase>, use_as: bool) -> fmt::Result { | ||
1136 | if f.should_truncate() { | ||
1137 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
1138 | } | ||
1139 | |||
1140 | self.substs[0].hir_fmt(f)?; | ||
1141 | if use_as { | ||
1142 | write!(f, " as ")?; | ||
1143 | } else { | ||
1144 | write!(f, ": ")?; | ||
1145 | } | ||
1146 | write!(f, "{}", f.db.trait_data(self.trait_).name.clone())?; | ||
1147 | if self.substs.len() > 1 { | ||
1148 | write!(f, "<")?; | ||
1149 | f.write_joined(&self.substs[1..], ", ")?; | ||
1150 | write!(f, ">")?; | ||
1151 | } | ||
1152 | Ok(()) | ||
1153 | } | ||
1154 | } | ||
1155 | |||
1156 | impl HirDisplay for TraitRef { | ||
1157 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
1158 | self.hir_fmt_ext(f, false) | ||
1159 | } | ||
1160 | } | ||
1161 | |||
1162 | impl HirDisplay for &GenericPredicate { | ||
1163 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
1164 | HirDisplay::hir_fmt(*self, f) | ||
1165 | } | ||
1166 | } | ||
1167 | |||
1168 | impl HirDisplay for GenericPredicate { | ||
1169 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
1170 | if f.should_truncate() { | ||
1171 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
1172 | } | ||
1173 | |||
1174 | match self { | ||
1175 | GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, | ||
1176 | GenericPredicate::Projection(projection_pred) => { | ||
1177 | write!(f, "<")?; | ||
1178 | projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?; | ||
1179 | write!( | ||
1180 | f, | ||
1181 | ">::{} = {}", | ||
1182 | f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name, | ||
1183 | projection_pred.ty.display(f.db) | ||
1184 | )?; | ||
1185 | } | ||
1186 | GenericPredicate::Error => write!(f, "{{error}}")?, | ||
1187 | } | ||
1188 | Ok(()) | ||
1189 | } | ||
1190 | } | ||
1191 | |||
1192 | impl HirDisplay for Obligation { | ||
1193 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
1194 | match self { | ||
1195 | Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), | ||
1196 | Obligation::Projection(proj) => write!( | ||
1197 | f, | ||
1198 | "Normalize({} => {})", | ||
1199 | proj.projection_ty.display(f.db), | ||
1200 | proj.ty.display(f.db) | ||
1201 | ), | ||
1202 | } | ||
1203 | } | ||
1204 | } | ||
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index c68c5852b..6a2aded02 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -14,9 +14,9 @@ use hir_def::{ | |||
14 | path::{GenericArg, Path, PathSegment, PathSegments}, | 14 | path::{GenericArg, Path, PathSegment, PathSegments}, |
15 | resolver::{HasResolver, Resolver, TypeNs}, | 15 | resolver::{HasResolver, Resolver, TypeNs}, |
16 | type_ref::{TypeBound, TypeRef}, | 16 | type_ref::{TypeBound, TypeRef}, |
17 | AdtId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, | 17 | AdtId, AssocContainerId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, |
18 | LocalStructFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, | 18 | ImplId, LocalStructFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, |
19 | VariantId, | 19 | UnionId, VariantId, |
20 | }; | 20 | }; |
21 | use ra_arena::map::ArenaMap; | 21 | use ra_arena::map::ArenaMap; |
22 | use ra_db::CrateId; | 22 | use ra_db::CrateId; |
@@ -152,7 +152,7 @@ impl Ty { | |||
152 | data.provenance == TypeParamProvenance::ArgumentImplTrait | 152 | data.provenance == TypeParamProvenance::ArgumentImplTrait |
153 | }) | 153 | }) |
154 | .nth(idx as usize) | 154 | .nth(idx as usize) |
155 | .map_or(Ty::Unknown, |(id, _)| Ty::Param(id)); | 155 | .map_or(Ty::Unknown, |(id, _)| Ty::Placeholder(id)); |
156 | param | 156 | param |
157 | } else { | 157 | } else { |
158 | Ty::Unknown | 158 | Ty::Unknown |
@@ -270,7 +270,7 @@ impl Ty { | |||
270 | let generics = | 270 | let generics = |
271 | generics(ctx.db, ctx.resolver.generic_def().expect("generics in scope")); | 271 | generics(ctx.db, ctx.resolver.generic_def().expect("generics in scope")); |
272 | match ctx.type_param_mode { | 272 | match ctx.type_param_mode { |
273 | TypeParamLoweringMode::Placeholder => Ty::Param(param_id) |