diff options
Diffstat (limited to 'crates')
93 files changed, 6163 insertions, 4351 deletions
diff --git a/crates/ra_flycheck/Cargo.toml b/crates/flycheck/Cargo.toml index 1aa39bade..dc26b8ce7 100644 --- a/crates/ra_flycheck/Cargo.toml +++ b/crates/flycheck/Cargo.toml | |||
@@ -1,6 +1,6 @@ | |||
1 | [package] | 1 | [package] |
2 | edition = "2018" | 2 | edition = "2018" |
3 | name = "ra_flycheck" | 3 | name = "flycheck" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | 6 | ||
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 6c4170529..92ec4f92e 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs | |||
@@ -3,14 +3,14 @@ | |||
3 | //! LSP diagnostics based on the output of the command. | 3 | //! LSP diagnostics based on the output of the command. |
4 | 4 | ||
5 | use std::{ | 5 | use std::{ |
6 | fmt, | ||
6 | io::{self, BufReader}, | 7 | io::{self, BufReader}, |
7 | path::PathBuf, | 8 | path::PathBuf, |
8 | process::{Command, Stdio}, | 9 | process::{Command, Stdio}, |
9 | time::Instant, | 10 | time::Duration, |
10 | }; | 11 | }; |
11 | 12 | ||
12 | use cargo_metadata::Message; | 13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; |
13 | use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender}; | ||
14 | 14 | ||
15 | pub use cargo_metadata::diagnostic::{ | 15 | pub use cargo_metadata::diagnostic::{ |
16 | Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion, | 16 | Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion, |
@@ -31,171 +31,142 @@ pub enum FlycheckConfig { | |||
31 | }, | 31 | }, |
32 | } | 32 | } |
33 | 33 | ||
34 | impl fmt::Display for FlycheckConfig { | ||
35 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
36 | match self { | ||
37 | FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command), | ||
38 | FlycheckConfig::CustomCommand { command, args } => { | ||
39 | write!(f, "{} {}", command, args.join(" ")) | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
34 | /// Flycheck wraps the shared state and communication machinery used for | 45 | /// Flycheck wraps the shared state and communication machinery used for |
35 | /// running `cargo check` (or other compatible command) and providing | 46 | /// running `cargo check` (or other compatible command) and providing |
36 | /// diagnostics based on the output. | 47 | /// diagnostics based on the output. |
37 | /// The spawned thread is shut down when this struct is dropped. | 48 | /// The spawned thread is shut down when this struct is dropped. |
38 | #[derive(Debug)] | 49 | #[derive(Debug)] |
39 | pub struct Flycheck { | 50 | pub struct FlycheckHandle { |
40 | // XXX: drop order is significant | 51 | // XXX: drop order is significant |
41 | cmd_send: Sender<CheckCommand>, | 52 | cmd_send: Sender<Restart>, |
42 | handle: jod_thread::JoinHandle<()>, | 53 | handle: jod_thread::JoinHandle, |
43 | pub task_recv: Receiver<CheckTask>, | ||
44 | } | 54 | } |
45 | 55 | ||
46 | impl Flycheck { | 56 | impl FlycheckHandle { |
47 | pub fn new(config: FlycheckConfig, workspace_root: PathBuf) -> Flycheck { | 57 | pub fn spawn( |
48 | let (task_send, task_recv) = unbounded::<CheckTask>(); | 58 | sender: Box<dyn Fn(Message) + Send>, |
49 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); | 59 | config: FlycheckConfig, |
60 | workspace_root: PathBuf, | ||
61 | ) -> FlycheckHandle { | ||
62 | let (cmd_send, cmd_recv) = unbounded::<Restart>(); | ||
50 | let handle = jod_thread::spawn(move || { | 63 | let handle = jod_thread::spawn(move || { |
51 | FlycheckThread::new(config, workspace_root).run(&task_send, &cmd_recv); | 64 | FlycheckActor::new(sender, config, workspace_root).run(cmd_recv); |
52 | }); | 65 | }); |
53 | Flycheck { task_recv, cmd_send, handle } | 66 | FlycheckHandle { cmd_send, handle } |
54 | } | 67 | } |
55 | 68 | ||
56 | /// Schedule a re-start of the cargo check worker. | 69 | /// Schedule a re-start of the cargo check worker. |
57 | pub fn update(&self) { | 70 | pub fn update(&self) { |
58 | self.cmd_send.send(CheckCommand::Update).unwrap(); | 71 | self.cmd_send.send(Restart).unwrap(); |
59 | } | 72 | } |
60 | } | 73 | } |
61 | 74 | ||
62 | #[derive(Debug)] | 75 | #[derive(Debug)] |
63 | pub enum CheckTask { | 76 | pub enum Message { |
64 | /// Request a clearing of all cached diagnostics from the check watcher | ||
65 | ClearDiagnostics, | ||
66 | |||
67 | /// Request adding a diagnostic with fixes included to a file | 77 | /// Request adding a diagnostic with fixes included to a file |
68 | AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, | 78 | AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, |
69 | 79 | ||
70 | /// Request check progress notification to client | 80 | /// Request check progress notification to client |
71 | Status(Status), | 81 | Progress(Progress), |
72 | } | 82 | } |
73 | 83 | ||
74 | #[derive(Debug)] | 84 | #[derive(Debug)] |
75 | pub enum Status { | 85 | pub enum Progress { |
76 | Being, | 86 | DidStart, |
77 | Progress(String), | 87 | DidCheckCrate(String), |
78 | End, | 88 | DidFinish, |
89 | DidCancel, | ||
79 | } | 90 | } |
80 | 91 | ||
81 | pub enum CheckCommand { | 92 | struct Restart; |
82 | /// Request re-start of check thread | ||
83 | Update, | ||
84 | } | ||
85 | 93 | ||
86 | struct FlycheckThread { | 94 | struct FlycheckActor { |
95 | sender: Box<dyn Fn(Message) + Send>, | ||
87 | config: FlycheckConfig, | 96 | config: FlycheckConfig, |
88 | workspace_root: PathBuf, | 97 | workspace_root: PathBuf, |
89 | last_update_req: Option<Instant>, | ||
90 | // XXX: drop order is significant | ||
91 | message_recv: Receiver<CheckEvent>, | ||
92 | /// WatchThread exists to wrap around the communication needed to be able to | 98 | /// WatchThread exists to wrap around the communication needed to be able to |
93 | /// run `cargo check` without blocking. Currently the Rust standard library | 99 | /// run `cargo check` without blocking. Currently the Rust standard library |
94 | /// doesn't provide a way to read sub-process output without blocking, so we | 100 | /// doesn't provide a way to read sub-process output without blocking, so we |
95 | /// have to wrap sub-processes output handling in a thread and pass messages | 101 | /// have to wrap sub-processes output handling in a thread and pass messages |
96 | /// back over a channel. | 102 | /// back over a channel. |
97 | check_process: Option<jod_thread::JoinHandle<()>>, | 103 | // XXX: drop order is significant |
104 | check_process: Option<(Receiver<cargo_metadata::Message>, jod_thread::JoinHandle)>, | ||
98 | } | 105 | } |
99 | 106 | ||
100 | impl FlycheckThread { | 107 | enum Event { |
101 | fn new(config: FlycheckConfig, workspace_root: PathBuf) -> FlycheckThread { | 108 | Restart(Restart), |
102 | FlycheckThread { | 109 | CheckEvent(Option<cargo_metadata::Message>), |
103 | config, | 110 | } |
104 | workspace_root, | ||
105 | last_update_req: None, | ||
106 | message_recv: never(), | ||
107 | check_process: None, | ||
108 | } | ||
109 | } | ||
110 | |||
111 | fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) { | ||
112 | // If we rerun the thread, we need to discard the previous check results first | ||
113 | self.clean_previous_results(task_send); | ||
114 | |||
115 | loop { | ||
116 | select! { | ||
117 | recv(&cmd_recv) -> cmd => match cmd { | ||
118 | Ok(cmd) => self.handle_command(cmd), | ||
119 | Err(RecvError) => { | ||
120 | // Command channel has closed, so shut down | ||
121 | break; | ||
122 | }, | ||
123 | }, | ||
124 | recv(self.message_recv) -> msg => match msg { | ||
125 | Ok(msg) => self.handle_message(msg, task_send), | ||
126 | Err(RecvError) => { | ||
127 | // Watcher finished, replace it with a never channel to | ||
128 | // avoid busy-waiting. | ||
129 | self.message_recv = never(); | ||
130 | self.check_process = None; | ||
131 | }, | ||
132 | } | ||
133 | }; | ||
134 | |||
135 | if self.should_recheck() { | ||
136 | self.last_update_req = None; | ||
137 | task_send.send(CheckTask::ClearDiagnostics).unwrap(); | ||
138 | self.restart_check_process(); | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | 111 | ||
143 | fn clean_previous_results(&self, task_send: &Sender<CheckTask>) { | 112 | impl FlycheckActor { |
144 | task_send.send(CheckTask::ClearDiagnostics).unwrap(); | 113 | fn new( |
145 | task_send.send(CheckTask::Status(Status::End)).unwrap(); | 114 | sender: Box<dyn Fn(Message) + Send>, |
115 | config: FlycheckConfig, | ||
116 | workspace_root: PathBuf, | ||
117 | ) -> FlycheckActor { | ||
118 | FlycheckActor { sender, config, workspace_root, check_process: None } | ||
146 | } | 119 | } |
147 | 120 | fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> { | |
148 | fn should_recheck(&mut self) -> bool { | 121 | let check_chan = self.check_process.as_ref().map(|(chan, _thread)| chan); |
149 | if let Some(_last_update_req) = &self.last_update_req { | 122 | select! { |
150 | // We currently only request an update on save, as we need up to | 123 | recv(inbox) -> msg => msg.ok().map(Event::Restart), |
151 | // date source on disk for cargo check to do it's magic, so we | 124 | recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())), |
152 | // don't really need to debounce the requests at this point. | ||
153 | return true; | ||
154 | } | 125 | } |
155 | false | ||
156 | } | 126 | } |
157 | 127 | fn run(&mut self, inbox: Receiver<Restart>) { | |
158 | fn handle_command(&mut self, cmd: CheckCommand) { | 128 | while let Some(event) = self.next_event(&inbox) { |
159 | match cmd { | 129 | match event { |
160 | CheckCommand::Update => self.last_update_req = Some(Instant::now()), | 130 | Event::Restart(Restart) => { |
131 | while let Ok(Restart) = inbox.recv_timeout(Duration::from_millis(50)) {} | ||
132 | self.cancel_check_process(); | ||
133 | self.check_process = Some(self.start_check_process()); | ||
134 | self.send(Message::Progress(Progress::DidStart)); | ||
135 | } | ||
136 | Event::CheckEvent(None) => { | ||
137 | // Watcher finished, replace it with a never channel to | ||
138 | // avoid busy-waiting. | ||
139 | assert!(self.check_process.take().is_some()); | ||
140 | self.send(Message::Progress(Progress::DidFinish)); | ||
141 | } | ||
142 | Event::CheckEvent(Some(message)) => match message { | ||
143 | cargo_metadata::Message::CompilerArtifact(msg) => { | ||
144 | self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name))); | ||
145 | } | ||
146 | |||
147 | cargo_metadata::Message::CompilerMessage(msg) => { | ||
148 | self.send(Message::AddDiagnostic { | ||
149 | workspace_root: self.workspace_root.clone(), | ||
150 | diagnostic: msg.message, | ||
151 | }); | ||
152 | } | ||
153 | |||
154 | cargo_metadata::Message::BuildScriptExecuted(_) | ||
155 | | cargo_metadata::Message::BuildFinished(_) | ||
156 | | cargo_metadata::Message::TextLine(_) | ||
157 | | cargo_metadata::Message::Unknown => {} | ||
158 | }, | ||
159 | } | ||
161 | } | 160 | } |
161 | // If we rerun the thread, we need to discard the previous check results first | ||
162 | self.cancel_check_process(); | ||
162 | } | 163 | } |
163 | 164 | fn cancel_check_process(&mut self) { | |
164 | fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) { | 165 | if self.check_process.take().is_some() { |
165 | match msg { | 166 | self.send(Message::Progress(Progress::DidCancel)); |
166 | CheckEvent::Begin => { | ||
167 | task_send.send(CheckTask::Status(Status::Being)).unwrap(); | ||
168 | } | ||
169 | |||
170 | CheckEvent::End => { | ||
171 | task_send.send(CheckTask::Status(Status::End)).unwrap(); | ||
172 | } | ||
173 | |||
174 | CheckEvent::Msg(Message::CompilerArtifact(msg)) => { | ||
175 | task_send.send(CheckTask::Status(Status::Progress(msg.target.name))).unwrap(); | ||
176 | } | ||
177 | |||
178 | CheckEvent::Msg(Message::CompilerMessage(msg)) => { | ||
179 | task_send | ||
180 | .send(CheckTask::AddDiagnostic { | ||
181 | workspace_root: self.workspace_root.clone(), | ||
182 | diagnostic: msg.message, | ||
183 | }) | ||
184 | .unwrap(); | ||
185 | } | ||
186 | |||
187 | CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {} | ||
188 | CheckEvent::Msg(Message::BuildFinished(_)) => {} | ||
189 | CheckEvent::Msg(Message::TextLine(_)) => {} | ||
190 | CheckEvent::Msg(Message::Unknown) => {} | ||
191 | } | 167 | } |
192 | } | 168 | } |
193 | 169 | fn start_check_process(&self) -> (Receiver<cargo_metadata::Message>, jod_thread::JoinHandle) { | |
194 | fn restart_check_process(&mut self) { | ||
195 | // First, clear and cancel the old thread | ||
196 | self.message_recv = never(); | ||
197 | self.check_process = None; | ||
198 | |||
199 | let mut cmd = match &self.config { | 170 | let mut cmd = match &self.config { |
200 | FlycheckConfig::CargoCommand { | 171 | FlycheckConfig::CargoCommand { |
201 | command, | 172 | command, |
@@ -229,23 +200,22 @@ impl FlycheckThread { | |||
229 | cmd.current_dir(&self.workspace_root); | 200 | cmd.current_dir(&self.workspace_root); |
230 | 201 | ||
231 | let (message_send, message_recv) = unbounded(); | 202 | let (message_send, message_recv) = unbounded(); |
232 | self.message_recv = message_recv; | 203 | let thread = jod_thread::spawn(move || { |
233 | self.check_process = Some(jod_thread::spawn(move || { | ||
234 | // If we trigger an error here, we will do so in the loop instead, | 204 | // If we trigger an error here, we will do so in the loop instead, |
235 | // which will break out of the loop, and continue the shutdown | 205 | // which will break out of the loop, and continue the shutdown |
236 | let _ = message_send.send(CheckEvent::Begin); | ||
237 | |||
238 | let res = run_cargo(cmd, &mut |message| { | 206 | let res = run_cargo(cmd, &mut |message| { |
239 | // Skip certain kinds of messages to only spend time on what's useful | 207 | // Skip certain kinds of messages to only spend time on what's useful |
240 | match &message { | 208 | match &message { |
241 | Message::CompilerArtifact(artifact) if artifact.fresh => return true, | 209 | cargo_metadata::Message::CompilerArtifact(artifact) if artifact.fresh => { |
242 | Message::BuildScriptExecuted(_) => return true, | 210 | return true |
243 | Message::Unknown => return true, | 211 | } |
212 | cargo_metadata::Message::BuildScriptExecuted(_) | ||
213 | | cargo_metadata::Message::Unknown => return true, | ||
244 | _ => {} | 214 | _ => {} |
245 | } | 215 | } |
246 | 216 | ||
247 | // if the send channel was closed, we want to shutdown | 217 | // if the send channel was closed, we want to shutdown |
248 | message_send.send(CheckEvent::Msg(message)).is_ok() | 218 | message_send.send(message).is_ok() |
249 | }); | 219 | }); |
250 | 220 | ||
251 | if let Err(err) = res { | 221 | if let Err(err) = res { |
@@ -253,18 +223,13 @@ impl FlycheckThread { | |||
253 | // to display user-caused misconfiguration errors instead of just logging them here | 223 | // to display user-caused misconfiguration errors instead of just logging them here |
254 | log::error!("Cargo watcher failed {:?}", err); | 224 | log::error!("Cargo watcher failed {:?}", err); |
255 | } | 225 | } |
256 | 226 | }); | |
257 | // We can ignore any error here, as we are already in the progress | 227 | (message_recv, thread) |
258 | // of shutting down. | ||
259 | let _ = message_send.send(CheckEvent::End); | ||
260 | })) | ||
261 | } | 228 | } |
262 | } | ||
263 | 229 | ||
264 | enum CheckEvent { | 230 | fn send(&self, check_task: Message) { |
265 | Begin, | 231 | (self.sender)(check_task) |
266 | Msg(cargo_metadata::Message), | 232 | } |
267 | End, | ||
268 | } | 233 | } |
269 | 234 | ||
270 | fn run_cargo( | 235 | fn run_cargo( |
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index 45b19c45a..1b259682d 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs | |||
@@ -28,6 +28,12 @@ impl AsRef<Path> for AbsPathBuf { | |||
28 | } | 28 | } |
29 | } | 29 | } |
30 | 30 | ||
31 | impl AsRef<AbsPath> for AbsPathBuf { | ||
32 | fn as_ref(&self) -> &AbsPath { | ||
33 | self.as_path() | ||
34 | } | ||
35 | } | ||
36 | |||
31 | impl TryFrom<PathBuf> for AbsPathBuf { | 37 | impl TryFrom<PathBuf> for AbsPathBuf { |
32 | type Error = PathBuf; | 38 | type Error = PathBuf; |
33 | fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> { | 39 | fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> { |
@@ -45,9 +51,19 @@ impl TryFrom<&str> for AbsPathBuf { | |||
45 | } | 51 | } |
46 | } | 52 | } |
47 | 53 | ||
54 | impl PartialEq<AbsPath> for AbsPathBuf { | ||
55 | fn eq(&self, other: &AbsPath) -> bool { | ||
56 | self.as_path() == other | ||
57 | } | ||
58 | } | ||
59 | |||
48 | impl AbsPathBuf { | 60 | impl AbsPathBuf { |
61 | pub fn assert(path: PathBuf) -> AbsPathBuf { | ||
62 | AbsPathBuf::try_from(path) | ||
63 | .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) | ||
64 | } | ||
49 | pub fn as_path(&self) -> &AbsPath { | 65 | pub fn as_path(&self) -> &AbsPath { |
50 | AbsPath::new_unchecked(self.0.as_path()) | 66 | AbsPath::assert(self.0.as_path()) |
51 | } | 67 | } |
52 | pub fn pop(&mut self) -> bool { | 68 | pub fn pop(&mut self) -> bool { |
53 | self.0.pop() | 69 | self.0.pop() |
@@ -77,15 +93,19 @@ impl<'a> TryFrom<&'a Path> for &'a AbsPath { | |||
77 | if !path.is_absolute() { | 93 | if !path.is_absolute() { |
78 | return Err(path); | 94 | return Err(path); |
79 | } | 95 | } |
80 | Ok(AbsPath::new_unchecked(path)) | 96 | Ok(AbsPath::assert(path)) |
81 | } | 97 | } |
82 | } | 98 | } |
83 | 99 | ||
84 | impl AbsPath { | 100 | impl AbsPath { |
85 | fn new_unchecked(path: &Path) -> &AbsPath { | 101 | pub fn assert(path: &Path) -> &AbsPath { |
102 | assert!(path.is_absolute()); | ||
86 | unsafe { &*(path as *const Path as *const AbsPath) } | 103 | unsafe { &*(path as *const Path as *const AbsPath) } |
87 | } | 104 | } |
88 | 105 | ||
106 | pub fn parent(&self) -> Option<&AbsPath> { | ||
107 | self.0.parent().map(AbsPath::assert) | ||
108 | } | ||
89 | pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { | 109 | pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { |
90 | self.as_ref().join(path).try_into().unwrap() | 110 | self.as_ref().join(path).try_into().unwrap() |
91 | } | 111 | } |
diff --git a/crates/ra_arena/src/lib.rs b/crates/ra_arena/src/lib.rs index 441fbb3cb..3169aa5b8 100644 --- a/crates/ra_arena/src/lib.rs +++ b/crates/ra_arena/src/lib.rs | |||
@@ -116,6 +116,9 @@ impl<T> Arena<T> { | |||
116 | ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator { | 116 | ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator { |
117 | self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawId(idx as u32)), value)) | 117 | self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawId(idx as u32)), value)) |
118 | } | 118 | } |
119 | pub fn shrink_to_fit(&mut self) { | ||
120 | self.data.shrink_to_fit(); | ||
121 | } | ||
119 | } | 122 | } |
120 | 123 | ||
121 | impl<T> Default for Arena<T> { | 124 | impl<T> Default for Arena<T> { |
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs index 88b62278f..96affe49d 100644 --- a/crates/ra_assists/src/handlers/introduce_variable.rs +++ b/crates/ra_assists/src/handlers/introduce_variable.rs | |||
@@ -44,12 +44,26 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
44 | } | 44 | } |
45 | let target = expr.syntax().text_range(); | 45 | let target = expr.syntax().text_range(); |
46 | acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { | 46 | acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { |
47 | let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) { | ||
48 | Some(field) => field.name_ref(), | ||
49 | None => None, | ||
50 | }; | ||
51 | |||
47 | let mut buf = String::new(); | 52 | let mut buf = String::new(); |
48 | 53 | ||
54 | let var_name = match &field_shorthand { | ||
55 | Some(it) => it.to_string(), | ||
56 | None => "var_name".to_string(), | ||
57 | }; | ||
58 | let expr_range = match &field_shorthand { | ||
59 | Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()), | ||
60 | None => expr.syntax().text_range(), | ||
61 | }; | ||
62 | |||
49 | if wrap_in_block { | 63 | if wrap_in_block { |
50 | buf.push_str("{ let var_name = "); | 64 | format_to!(buf, "{{ let {} = ", var_name); |
51 | } else { | 65 | } else { |
52 | buf.push_str("let var_name = "); | 66 | format_to!(buf, "let {} = ", var_name); |
53 | }; | 67 | }; |
54 | format_to!(buf, "{}", expr.syntax()); | 68 | format_to!(buf, "{}", expr.syntax()); |
55 | 69 | ||
@@ -64,13 +78,13 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
64 | if full_stmt.unwrap().semicolon_token().is_none() { | 78 | if full_stmt.unwrap().semicolon_token().is_none() { |
65 | buf.push_str(";"); | 79 | buf.push_str(";"); |
66 | } | 80 | } |
67 | let offset = expr.syntax().text_range(); | ||
68 | match ctx.config.snippet_cap { | 81 | match ctx.config.snippet_cap { |
69 | Some(cap) => { | 82 | Some(cap) => { |
70 | let snip = buf.replace("let var_name", "let $0var_name"); | 83 | let snip = |
71 | edit.replace_snippet(cap, offset, snip) | 84 | buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name)); |
85 | edit.replace_snippet(cap, expr_range, snip) | ||
72 | } | 86 | } |
73 | None => edit.replace(offset, buf), | 87 | None => edit.replace(expr_range, buf), |
74 | } | 88 | } |
75 | return; | 89 | return; |
76 | } | 90 | } |
@@ -88,11 +102,12 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
88 | buf.push_str(text); | 102 | buf.push_str(text); |
89 | } | 103 | } |
90 | 104 | ||
91 | edit.replace(expr.syntax().text_range(), "var_name".to_string()); | 105 | edit.replace(expr_range, var_name.clone()); |
92 | let offset = anchor_stmt.text_range().start(); | 106 | let offset = anchor_stmt.text_range().start(); |
93 | match ctx.config.snippet_cap { | 107 | match ctx.config.snippet_cap { |
94 | Some(cap) => { | 108 | Some(cap) => { |
95 | let snip = buf.replace("let var_name", "let $0var_name"); | 109 | let snip = |
110 | buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name)); | ||
96 | edit.insert_snippet(cap, offset, snip) | 111 | edit.insert_snippet(cap, offset, snip) |
97 | } | 112 | } |
98 | None => edit.insert(offset, buf), | 113 | None => edit.insert(offset, buf), |
@@ -504,6 +519,32 @@ fn main() { | |||
504 | } | 519 | } |
505 | 520 | ||
506 | #[test] | 521 | #[test] |
522 | fn introduce_var_field_shorthand() { | ||
523 | check_assist( | ||
524 | introduce_variable, | ||
525 | r#" | ||
526 | struct S { | ||
527 | foo: i32 | ||
528 | } | ||
529 | |||
530 | fn main() { | ||
531 | S { foo: <|>1 + 1<|> } | ||
532 | } | ||
533 | "#, | ||
534 | r#" | ||
535 | struct S { | ||
536 | foo: i32 | ||
537 | } | ||
538 | |||
539 | fn main() { | ||
540 | let $0foo = 1 + 1; | ||
541 | S { foo } | ||
542 | } | ||
543 | "#, | ||
544 | ) | ||
545 | } | ||
546 | |||
547 | #[test] | ||
507 | fn test_introduce_var_for_return_not_applicable() { | 548 | fn test_introduce_var_for_return_not_applicable() { |
508 | check_assist_not_applicable(introduce_variable, "fn foo() { <|>return<|>; } "); | 549 | check_assist_not_applicable(introduce_variable, "fn foo() { <|>return<|>; } "); |
509 | } | 550 | } |
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index ddf46e6c4..4f4fb4494 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs | |||
@@ -132,10 +132,17 @@ fn with_files( | |||
132 | 132 | ||
133 | let mut file_position = None; | 133 | let mut file_position = None; |
134 | 134 | ||
135 | for entry in fixture.iter() { | 135 | for entry in fixture { |
136 | let meta = match ParsedMeta::from(entry) { | 136 | let text = if entry.text.contains(CURSOR_MARKER) { |
137 | ParsedMeta::File(it) => it, | 137 | let (range_or_offset, text) = extract_range_or_offset(&entry.text); |
138 | assert!(file_position.is_none()); | ||
139 | file_position = Some((file_id, range_or_offset)); | ||
140 | text.to_string() | ||
141 | } else { | ||
142 | entry.text.clone() | ||
138 | }; | 143 | }; |
144 | |||
145 | let meta = FileMeta::from(entry); | ||
139 | assert!(meta.path.starts_with(&source_root_prefix)); | 146 | assert!(meta.path.starts_with(&source_root_prefix)); |
140 | 147 | ||
141 | if let Some(krate) = meta.krate { | 148 | if let Some(krate) = meta.krate { |
@@ -157,15 +164,6 @@ fn with_files( | |||
157 | default_crate_root = Some(file_id); | 164 | default_crate_root = Some(file_id); |
158 | } | 165 | } |
159 | 166 | ||
160 | let text = if entry.text.contains(CURSOR_MARKER) { | ||
161 | let (range_or_offset, text) = extract_range_or_offset(&entry.text); | ||
162 | assert!(file_position.is_none()); | ||
163 | file_position = Some((file_id, range_or_offset)); | ||
164 | text.to_string() | ||
165 | } else { | ||
166 | entry.text.to_string() | ||
167 | }; | ||
168 | |||
169 | db.set_file_text(file_id, Arc::new(text)); | 167 | db.set_file_text(file_id, Arc::new(text)); |
170 | db.set_file_source_root(file_id, source_root_id); | 168 | db.set_file_source_root(file_id, source_root_id); |
171 | let path = VfsPath::new_virtual_path(meta.path); | 169 | let path = VfsPath::new_virtual_path(meta.path); |
@@ -198,10 +196,6 @@ fn with_files( | |||
198 | (file_position, files) | 196 | (file_position, files) |
199 | } | 197 | } |
200 | 198 | ||
201 | enum ParsedMeta { | ||
202 | File(FileMeta), | ||
203 | } | ||
204 | |||
205 | struct FileMeta { | 199 | struct FileMeta { |
206 | path: String, | 200 | path: String, |
207 | krate: Option<String>, | 201 | krate: Option<String>, |
@@ -211,22 +205,22 @@ struct FileMeta { | |||
211 | env: Env, | 205 | env: Env, |
212 | } | 206 | } |
213 | 207 | ||
214 | impl From<&Fixture> for ParsedMeta { | 208 | impl From<Fixture> for FileMeta { |
215 | fn from(f: &Fixture) -> Self { | 209 | fn from(f: Fixture) -> FileMeta { |
216 | let mut cfg = CfgOptions::default(); | 210 | let mut cfg = CfgOptions::default(); |
217 | f.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into())); | 211 | f.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into())); |
218 | f.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into())); | 212 | f.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into())); |
219 | 213 | ||
220 | Self::File(FileMeta { | 214 | FileMeta { |
221 | path: f.path.to_owned(), | 215 | path: f.path, |
222 | krate: f.crate_name.to_owned(), | 216 | krate: f.krate, |
223 | deps: f.deps.to_owned(), | 217 | deps: f.deps, |
224 | cfg, | 218 | cfg, |
225 | edition: f | 219 | edition: f |
226 | .edition | 220 | .edition |
227 | .as_ref() | 221 | .as_ref() |
228 | .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), | 222 | .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), |
229 | env: Env::from(f.env.iter()), | 223 | env: Env::from(f.env.iter()), |
230 | }) | 224 | } |
231 | } | 225 | } |
232 | } | 226 | } |
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index 875290259..4a3ba57da 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs | |||
@@ -114,7 +114,7 @@ pub trait SourceDatabase: CheckCanceled + FileLoader + std::fmt::Debug { | |||
114 | } | 114 | } |
115 | 115 | ||
116 | fn parse_query(db: &impl SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> { | 116 | fn parse_query(db: &impl SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> { |
117 | let _p = profile("parse_query"); | 117 | let _p = profile("parse_query").detail(|| format!("{:?}", file_id)); |
118 | let text = db.file_text(file_id); | 118 | let text = db.file_text(file_id); |
119 | SourceFile::parse(&*text) | 119 | SourceFile::parse(&*text) |
120 | } | 120 | } |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index ffd5278ec..a379b9f49 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -31,10 +31,7 @@ use hir_ty::{ | |||
31 | }; | 31 | }; |
32 | use ra_db::{CrateId, CrateName, Edition, FileId}; | 32 | use ra_db::{CrateId, CrateName, Edition, FileId}; |
33 | use ra_prof::profile; | 33 | use ra_prof::profile; |
34 | use ra_syntax::{ | 34 | use ra_syntax::ast::{self, AttrsOwner, NameOwner}; |
35 | ast::{self, AttrsOwner, NameOwner}, | ||
36 | AstNode, | ||
37 | }; | ||
38 | use rustc_hash::FxHashSet; | 35 | use rustc_hash::FxHashSet; |
39 | 36 | ||
40 | use crate::{ | 37 | use crate::{ |
@@ -205,7 +202,8 @@ impl ModuleDef { | |||
205 | } | 202 | } |
206 | 203 | ||
207 | pub use hir_def::{ | 204 | pub use hir_def::{ |
208 | attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocItemId, AssocItemLoc, | 205 | attr::Attrs, item_scope::ItemInNs, item_tree::ItemTreeNode, visibility::Visibility, |
206 | AssocItemId, AssocItemLoc, | ||
209 | }; | 207 | }; |
210 | 208 | ||
211 | impl Module { | 209 | impl Module { |
@@ -872,7 +870,7 @@ where | |||
872 | ID: Lookup<Data = AssocItemLoc<AST>>, | 870 | ID: Lookup<Data = AssocItemLoc<AST>>, |
873 | DEF: From<ID>, | 871 | DEF: From<ID>, |
874 | CTOR: FnOnce(DEF) -> AssocItem, | 872 | CTOR: FnOnce(DEF) -> AssocItem, |
875 | AST: AstNode, | 873 | AST: ItemTreeNode, |
876 | { | 874 | { |
877 | match id.lookup(db.upcast()).container { | 875 | match id.lookup(db.upcast()).container { |
878 | AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))), | 876 | AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))), |
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index b25dac28e..bb67952de 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -6,7 +6,7 @@ pub use hir_def::db::{ | |||
6 | ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, | 6 | ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, |
7 | InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, | 7 | InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, |
8 | InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, | 8 | InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, |
9 | InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, | 9 | InternUnionQuery, ItemTreeQuery, LangItemQuery, ModuleLangItemsQuery, StaticDataQuery, |
10 | StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, | 10 | StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, |
11 | }; | 11 | }; |
12 | pub use hir_expand::db::{ | 12 | pub use hir_expand::db::{ |
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index ef1f65ee0..6d43924e3 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml | |||
@@ -17,6 +17,7 @@ drop_bomb = "0.1.4" | |||
17 | fst = { version = "0.4", default-features = false } | 17 | fst = { version = "0.4", default-features = false } |
18 | itertools = "0.9.0" | 18 | itertools = "0.9.0" |
19 | indexmap = "1.4.0" | 19 | indexmap = "1.4.0" |
20 | smallvec = "1.4.0" | ||
20 | 21 | ||
21 | stdx = { path = "../stdx" } | 22 | stdx = { path = "../stdx" } |
22 | 23 | ||
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 2bc34d449..4994a2125 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs | |||
@@ -8,12 +8,12 @@ use hir_expand::{ | |||
8 | InFile, | 8 | InFile, |
9 | }; | 9 | }; |
10 | use ra_arena::{map::ArenaMap, Arena}; | 10 | use ra_arena::{map::ArenaMap, Arena}; |
11 | use ra_prof::profile; | ||
12 | use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner}; | 11 | use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner}; |
13 | 12 | ||
14 | use crate::{ | 13 | use crate::{ |
15 | body::{CfgExpander, LowerCtx}, | 14 | body::{CfgExpander, LowerCtx}, |
16 | db::DefDatabase, | 15 | db::DefDatabase, |
16 | item_tree::{Field, Fields, ItemTree}, | ||
17 | src::HasChildSource, | 17 | src::HasChildSource, |
18 | src::HasSource, | 18 | src::HasSource, |
19 | trace::Trace, | 19 | trace::Trace, |
@@ -22,6 +22,7 @@ use crate::{ | |||
22 | EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, | 22 | EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, |
23 | VariantId, | 23 | VariantId, |
24 | }; | 24 | }; |
25 | use ra_cfg::CfgOptions; | ||
25 | 26 | ||
26 | /// Note that we use `StructData` for unions as well! | 27 | /// Note that we use `StructData` for unions as well! |
27 | #[derive(Debug, Clone, PartialEq, Eq)] | 28 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -59,39 +60,48 @@ pub struct FieldData { | |||
59 | 60 | ||
60 | impl StructData { | 61 | impl StructData { |
61 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { | 62 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { |
62 | let src = id.lookup(db).source(db); | 63 | let loc = id.lookup(db); |
64 | let item_tree = db.item_tree(loc.id.file_id); | ||
65 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); | ||
63 | 66 | ||
64 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); | 67 | let strukt = &item_tree[loc.id.value]; |
65 | let variant_data = | 68 | let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields); |
66 | VariantData::new(db, src.map(|s| s.kind()), id.lookup(db).container.module(db)); | 69 | |
67 | let variant_data = Arc::new(variant_data); | 70 | Arc::new(StructData { name: strukt.name.clone(), variant_data: Arc::new(variant_data) }) |
68 | Arc::new(StructData { name, variant_data }) | ||
69 | } | 71 | } |
70 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { | 72 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { |
71 | let src = id.lookup(db).source(db); | 73 | let loc = id.lookup(db); |
72 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); | 74 | let item_tree = db.item_tree(loc.id.file_id); |
73 | let variant_data = VariantData::new( | 75 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); |
74 | db, | 76 | |
75 | src.map(|s| { | 77 | let union = &item_tree[loc.id.value]; |
76 | s.record_field_def_list() | 78 | let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields); |
77 | .map(ast::StructKind::Record) | 79 | |
78 | .unwrap_or(ast::StructKind::Unit) | 80 | Arc::new(StructData { name: union.name.clone(), variant_data: Arc::new(variant_data) }) |
79 | }), | ||
80 | id.lookup(db).container.module(db), | ||
81 | ); | ||
82 | let variant_data = Arc::new(variant_data); | ||
83 | Arc::new(StructData { name, variant_data }) | ||
84 | } | 81 | } |
85 | } | 82 | } |
86 | 83 | ||
87 | impl EnumData { | 84 | impl EnumData { |
88 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { | 85 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { |
89 | let _p = profile("enum_data_query"); | 86 | let loc = e.lookup(db); |
90 | let src = e.lookup(db).source(db); | 87 | let item_tree = db.item_tree(loc.id.file_id); |
91 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); | 88 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); |
92 | let mut trace = Trace::new_for_arena(); | 89 | |
93 | lower_enum(db, &mut trace, &src, e.lookup(db).container.module(db)); | 90 | let enum_ = &item_tree[loc.id.value]; |
94 | Arc::new(EnumData { name, variants: trace.into_arena() }) | 91 | let mut variants = Arena::new(); |
92 | for var_id in enum_.variants.clone() { | ||
93 | if item_tree.attrs(var_id.into()).is_cfg_enabled(&cfg_options) { | ||
94 | let var = &item_tree[var_id]; | ||
95 | let var_data = lower_fields(&item_tree, &cfg_options, &var.fields); | ||
96 | |||
97 | variants.alloc(EnumVariantData { | ||
98 | name: var.name.clone(), | ||
99 | variant_data: Arc::new(var_data), | ||
100 | }); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | Arc::new(EnumData { name: enum_.name.clone(), variants }) | ||
95 | } | 105 | } |
96 | 106 | ||
97 | pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> { | 107 | pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> { |
@@ -251,3 +261,35 @@ fn lower_struct( | |||
251 | ast::StructKind::Unit => StructKind::Unit, | 261 | ast::StructKind::Unit => StructKind::Unit, |
252 | } | 262 | } |
253 | } | 263 | } |
264 | |||
265 | fn lower_fields(item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields) -> VariantData { | ||
266 | match fields { | ||
267 | Fields::Record(flds) => { | ||
268 | let mut arena = Arena::new(); | ||
269 | for field_id in flds.clone() { | ||
270 | if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) { | ||
271 | arena.alloc(lower_field(item_tree, &item_tree[field_id])); | ||
272 | } | ||
273 | } | ||
274 | VariantData::Record(arena) | ||
275 | } | ||
276 | Fields::Tuple(flds) => { | ||
277 | let mut arena = Arena::new(); | ||
278 | for field_id in flds.clone() { | ||
279 | if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) { | ||
280 | arena.alloc(lower_field(item_tree, &item_tree[field_id])); | ||
281 | } | ||
282 | } | ||
283 | VariantData::Tuple(arena) | ||
284 | } | ||
285 | Fields::Unit => VariantData::Unit, | ||
286 | } | ||
287 | } | ||
288 | |||
289 | fn lower_field(item_tree: &ItemTree, field: &Field) -> FieldData { | ||
290 | FieldData { | ||
291 | name: field.name.clone(), | ||
292 | type_ref: field.type_ref.clone(), | ||
293 | visibility: item_tree[field.visibility].clone(), | ||
294 | } | ||
295 | } | ||
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs index 2eeba0572..e228e2145 100644 --- a/crates/ra_hir_def/src/attr.rs +++ b/crates/ra_hir_def/src/attr.rs | |||
@@ -13,7 +13,11 @@ use ra_syntax::{ | |||
13 | use tt::Subtree; | 13 | use tt::Subtree; |
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | db::DefDatabase, nameres::ModuleSource, path::ModPath, src::HasChildSource, src::HasSource, | 16 | db::DefDatabase, |
17 | item_tree::{ItemTreeId, ItemTreeNode}, | ||
18 | nameres::ModuleSource, | ||
19 | path::ModPath, | ||
20 | src::HasChildSource, | ||
17 | AdtId, AttrDefId, Lookup, | 21 | AdtId, AttrDefId, Lookup, |
18 | }; | 22 | }; |
19 | 23 | ||
@@ -34,6 +38,8 @@ impl ops::Deref for Attrs { | |||
34 | } | 38 | } |
35 | 39 | ||
36 | impl Attrs { | 40 | impl Attrs { |
41 | pub const EMPTY: Attrs = Attrs { entries: None }; | ||
42 | |||
37 | pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { | 43 | pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { |
38 | match def { | 44 | match def { |
39 | AttrDefId::ModuleId(module) => { | 45 | AttrDefId::ModuleId(module) => { |
@@ -65,19 +71,19 @@ impl Attrs { | |||
65 | Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner)) | 71 | Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner)) |
66 | } | 72 | } |
67 | AttrDefId::AdtId(it) => match it { | 73 | AttrDefId::AdtId(it) => match it { |
68 | AdtId::StructId(it) => attrs_from_loc(it.lookup(db), db), | 74 | AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
69 | AdtId::EnumId(it) => attrs_from_loc(it.lookup(db), db), | 75 | AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
70 | AdtId::UnionId(it) => attrs_from_loc(it.lookup(db), db), | 76 | AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
71 | }, | 77 | }, |
72 | AttrDefId::TraitId(it) => attrs_from_loc(it.lookup(db), db), | 78 | AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
73 | AttrDefId::MacroDefId(it) => { | 79 | AttrDefId::MacroDefId(it) => { |
74 | it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db)) | 80 | it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db)) |
75 | } | 81 | } |
76 | AttrDefId::ImplId(it) => attrs_from_loc(it.lookup(db), db), | 82 | AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
77 | AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db), | 83 | AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
78 | AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db), | 84 | AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
79 | AttrDefId::FunctionId(it) => attrs_from_loc(it.lookup(db), db), | 85 | AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
80 | AttrDefId::TypeAliasId(it) => attrs_from_loc(it.lookup(db), db), | 86 | AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
81 | } | 87 | } |
82 | } | 88 | } |
83 | 89 | ||
@@ -103,6 +109,18 @@ impl Attrs { | |||
103 | Attrs { entries } | 109 | Attrs { entries } |
104 | } | 110 | } |
105 | 111 | ||
112 | pub fn merge(&self, other: Attrs) -> Attrs { | ||
113 | match (&self.entries, &other.entries) { | ||
114 | (None, None) => Attrs { entries: None }, | ||
115 | (Some(entries), None) | (None, Some(entries)) => { | ||
116 | Attrs { entries: Some(entries.clone()) } | ||
117 | } | ||
118 | (Some(a), Some(b)) => { | ||
119 | Attrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) } | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | |||
106 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { | 124 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { |
107 | AttrQuery { attrs: self, key } | 125 | AttrQuery { attrs: self, key } |
108 | } | 126 | } |
@@ -187,11 +205,8 @@ where | |||
187 | Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) | 205 | Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) |
188 | } | 206 | } |
189 | 207 | ||
190 | fn attrs_from_loc<T>(node: T, db: &dyn DefDatabase) -> Attrs | 208 | fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> Attrs { |
191 | where | 209 | let tree = db.item_tree(id.file_id); |
192 | T: HasSource, | 210 | let mod_item = N::id_to_mod_item(id.value); |
193 | T::Value: ast::AttrsOwner, | 211 | tree.attrs(mod_item.into()).clone() |
194 | { | ||
195 | let src = node.source(db); | ||
196 | Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) | ||
197 | } | 212 | } |
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index f159f80af..a7e2e0982 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs | |||
@@ -27,6 +27,7 @@ use crate::{ | |||
27 | LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, | 27 | LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, |
28 | }, | 28 | }, |
29 | item_scope::BuiltinShadowMode, | 29 | item_scope::BuiltinShadowMode, |
30 | item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, | ||
30 | path::{GenericArgs, Path}, | 31 | path::{GenericArgs, Path}, |
31 | type_ref::{Mutability, Rawness, TypeRef}, | 32 | type_ref::{Mutability, Rawness, TypeRef}, |
32 | AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, | 33 | AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, |
@@ -35,6 +36,8 @@ use crate::{ | |||
35 | 36 | ||
36 | use super::{ExprSource, PatSource}; | 37 | use super::{ExprSource, PatSource}; |
37 | use ast::AstChildren; | 38 | use ast::AstChildren; |
39 | use rustc_hash::FxHashMap; | ||
40 | use std::{any::type_name, sync::Arc}; | ||
38 | 41 | ||
39 | pub(crate) struct LowerCtx { | 42 | pub(crate) struct LowerCtx { |
40 | hygiene: Hygiene, | 43 | hygiene: Hygiene, |
@@ -60,10 +63,10 @@ pub(super) fn lower( | |||
60 | params: Option<ast::ParamList>, | 63 | params: Option<ast::ParamList>, |
61 | body: Option<ast::Expr>, | 64 | body: Option<ast::Expr>, |
62 | ) -> (Body, BodySourceMap) { | 65 | ) -> (Body, BodySourceMap) { |
66 | let item_tree = db.item_tree(expander.current_file_id); | ||
63 | ExprCollector { | 67 | ExprCollector { |
64 | db, | 68 | db, |
65 | def, | 69 | def, |
66 | expander, | ||
67 | source_map: BodySourceMap::default(), | 70 | source_map: BodySourceMap::default(), |
68 | body: Body { | 71 | body: Body { |
69 | exprs: Arena::default(), | 72 | exprs: Arena::default(), |
@@ -72,6 +75,12 @@ pub(super) fn lower( | |||
72 | body_expr: dummy_expr_id(), | 75 | body_expr: dummy_expr_id(), |
73 | item_scope: Default::default(), | 76 | item_scope: Default::default(), |
74 | }, | 77 | }, |
78 | item_trees: { | ||
79 | let mut map = FxHashMap::default(); | ||
80 | map.insert(expander.current_file_id, item_tree); | ||
81 | map | ||
82 | }, | ||
83 | expander, | ||
75 | } | 84 | } |
76 | .collect(params, body) | 85 | .collect(params, body) |
77 | } | 86 | } |
@@ -82,6 +91,8 @@ struct ExprCollector<'a> { | |||
82 | expander: Expander, | 91 | expander: Expander, |
83 | body: Body, | 92 | body: Body, |
84 | source_map: BodySourceMap, | 93 | source_map: BodySourceMap, |
94 | |||
95 | item_trees: FxHashMap<HirFileId, Arc<ItemTree>>, | ||
85 | } | 96 | } |
86 | 97 | ||
87 | impl ExprCollector<'_> { | 98 | impl ExprCollector<'_> { |
@@ -533,6 +544,9 @@ impl ExprCollector<'_> { | |||
533 | self.source_map | 544 | self.source_map |
534 | .expansions | 545 | .expansions |
535 | .insert(macro_call, self.expander.current_file_id); | 546 | .insert(macro_call, self.expander.current_file_id); |
547 | |||
548 | let item_tree = self.db.item_tree(self.expander.current_file_id); | ||
549 | self.item_trees.insert(self.expander.current_file_id, item_tree); | ||
536 | let id = self.collect_expr(expansion); | 550 | let id = self.collect_expr(expansion); |
537 | self.expander.exit(self.db, mark); | 551 | self.expander.exit(self.db, mark); |
538 | id | 552 | id |
@@ -547,6 +561,32 @@ impl ExprCollector<'_> { | |||
547 | } | 561 | } |
548 | } | 562 | } |
549 | 563 | ||
564 | fn find_inner_item<N: ItemTreeNode>(&self, ast: &N::Source) -> Option<ItemTreeId<N>> { | ||
565 | let id = self.expander.ast_id(ast); | ||
566 | let tree = &self.item_trees[&id.file_id]; | ||
567 | |||
568 | // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes | ||
569 | |||
570 | // Root file (non-macro). | ||
571 | let item_tree_id = tree | ||
572 | .all_inner_items() | ||
573 | .chain(tree.top_level_items().iter().copied()) | ||
574 | .filter_map(|mod_item| mod_item.downcast::<N>()) | ||
575 | .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value.upcast()) | ||
576 | .or_else(|| { | ||
577 | log::debug!( | ||
578 | "couldn't find inner {} item for {:?} (AST: `{}` - {:?})", | ||
579 | type_name::<N>(), | ||
580 | id, | ||
581 | ast.syntax(), | ||
582 | ast.syntax(), | ||
583 | ); | ||
584 | None | ||
585 | })?; | ||
586 | |||
587 | Some(ItemTreeId::new(id.file_id, item_tree_id)) | ||
588 | } | ||
589 | |||
550 | fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { | 590 | fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { |
551 | if let Some(expr) = expr { | 591 | if let Some(expr) = expr { |
552 | self.collect_expr(expr) | 592 | self.collect_expr(expr) |
@@ -578,56 +618,65 @@ impl ExprCollector<'_> { | |||
578 | 618 | ||
579 | fn collect_block_items(&mut self, block: &ast::BlockExpr) { | 619 | fn collect_block_items(&mut self, block: &ast::BlockExpr) { |
580 | let container = ContainerId::DefWithBodyId(self.def); | 620 | let container = ContainerId::DefWithBodyId(self.def); |
581 | for item in block.items() { | 621 | |
582 | let (def, name): (ModuleDefId, Option<ast::Name>) = match item { | 622 | let items = block |
583 | ast::ModuleItem::FnDef(def) => { | 623 | .items() |
584 | let ast_id = self.expander.ast_id(&def); | 624 | .filter_map(|item| { |
585 | ( | 625 | let (def, name): (ModuleDefId, Option<ast::Name>) = match item { |
586 | FunctionLoc { container: container.into(), ast_id }.intern(self.db).into(), | 626 | ast::ModuleItem::FnDef(def) => { |
587 | def.name(), | 627 | let id = self.find_inner_item(&def)?; |
588 | ) | 628 | ( |
589 | } | 629 | FunctionLoc { container: container.into(), id }.intern(self.db).into(), |
590 | ast::ModuleItem::TypeAliasDef(def) => { | 630 | def.name(), |
591 | let ast_id = self.expander.ast_id(&def); | 631 | ) |
592 | ( | 632 | } |
593 | TypeAliasLoc { container: container.into(), ast_id }.intern(self.db).into(), | 633 | ast::ModuleItem::TypeAliasDef(def) => { |
594 | def.name(), | 634 | let id = self.find_inner_item(&def)?; |
595 | ) | 635 | ( |
596 | } | 636 | TypeAliasLoc { container: container.into(), id }.intern(self.db).into(), |
597 | ast::ModuleItem::ConstDef(def) => { | 637 | def.name(), |
598 | let ast_id = self.expander.ast_id(&def); | 638 | ) |
599 | ( | 639 | } |
600 | ConstLoc { container: container.into(), ast_id }.intern(self.db).into(), | 640 | ast::ModuleItem::ConstDef(def) => { |
601 | def.name(), | 641 | let id = self.find_inner_item(&def)?; |
602 | ) | 642 | ( |
603 | } | 643 | ConstLoc { container: container.into(), id }.intern(self.db).into(), |
604 | ast::ModuleItem::StaticDef(def) => { | 644 | def.name(), |
605 | let ast_id = self.expander.ast_id(&def); | 645 | ) |
606 | (StaticLoc { container, ast_id }.intern(self.db).into(), def.name()) | 646 | } |
607 | } | 647 | ast::ModuleItem::StaticDef(def) => { |
608 | ast::ModuleItem::StructDef(def) => { | 648 | let id = self.find_inner_item(&def)?; |
609 | let ast_id = self.expander.ast_id(&def); | 649 | (StaticLoc { container, id }.intern(self.db).into(), def.name()) |
610 | (StructLoc { container, ast_id }.intern(self.db).into(), def.name()) | 650 | } |
611 | } | 651 | ast::ModuleItem::StructDef(def) => { |
612 | ast::ModuleItem::EnumDef(def) => { | 652 | let id = self.find_inner_item(&def)?; |
613 | let ast_id = self.expander.ast_id(&def); | 653 | (StructLoc { container, id }.intern(self.db).into(), def.name()) |
614 | (EnumLoc { container, ast_id }.intern(self.db).into(), def.name()) | 654 | } |
615 | } | 655 | ast::ModuleItem::EnumDef(def) => { |
616 | ast::ModuleItem::UnionDef(def) => { | 656 | let id = self.find_inner_item(&def)?; |
617 | let ast_id = self.expander.ast_id(&def); | 657 | (EnumLoc { container, id }.intern(self.db).into(), def.name()) |
618 | (UnionLoc { container, ast_id }.intern(self.db).into(), def.name()) | 658 | } |
619 | } | 659 | ast::ModuleItem::UnionDef(def) => { |
620 | ast::ModuleItem::TraitDef(def) => { | 660 | let id = self.find_inner_item(&def)?; |
621 | let ast_id = self.expander.ast_id(&def); | 661 | (UnionLoc { container, id }.intern(self.db).into(), def.name()) |
622 | (TraitLoc { container, ast_id }.intern(self.db).into(), def.name()) | 662 | } |
623 | } | 663 | ast::ModuleItem::TraitDef(def) => { |
624 | ast::ModuleItem::ExternBlock(_) => continue, // FIXME: collect from extern blocks | 664 | let id = self.find_inner_item(&def)?; |
625 | ast::ModuleItem::ImplDef(_) | 665 | (TraitLoc { container, id }.intern(self.db).into(), def.name()) |
626 | | ast::ModuleItem::UseItem(_) | 666 | } |
627 | | ast::ModuleItem::ExternCrateItem(_) | 667 | ast::ModuleItem::ExternBlock(_) => return None, // FIXME: collect from extern blocks |
628 | | ast::ModuleItem::Module(_) | 668 | ast::ModuleItem::ImplDef(_) |
629 | | ast::ModuleItem::MacroCall(_) => continue, | 669 | | ast::ModuleItem::UseItem(_) |
630 | }; | 670 | | ast::ModuleItem::ExternCrateItem(_) |
671 | | ast::ModuleItem::Module(_) | ||
672 | | ast::ModuleItem::MacroCall(_) => return None, | ||
673 | }; | ||
674 | |||
675 | Some((def, name)) | ||
676 | }) | ||
677 | .collect::<Vec<_>>(); | ||
678 | |||
679 | for (def, name) in items { | ||
631 | self.body.item_scope.define_def(def); | 680 | self.body.item_scope.define_def(def); |
632 | if let Some(name) = name { | 681 | if let Some(name) = name { |
633 | let vis = crate::visibility::Visibility::Public; // FIXME determine correctly | 682 | let vis = crate::visibility::Visibility::Public; // FIXME determine correctly |
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs index 0b74199d9..99e876683 100644 --- a/crates/ra_hir_def/src/body/scope.rs +++ b/crates/ra_hir_def/src/body/scope.rs | |||
@@ -317,6 +317,39 @@ fn foo() { | |||
317 | ); | 317 | ); |
318 | } | 318 | } |
319 | 319 | ||
320 | #[test] | ||
321 | fn macro_inner_item() { | ||
322 | do_check( | ||
323 | r" | ||
324 | macro_rules! mac { | ||
325 | () => {{ | ||
326 | fn inner() {} | ||
327 | inner(); | ||
328 | }}; | ||
329 | } | ||
330 | |||
331 | fn foo() { | ||
332 | mac!(); | ||
333 | <|> | ||
334 | } | ||
335 | ", | ||
336 | &[], | ||
337 | ); | ||
338 | } | ||
339 | |||
340 | #[test] | ||
341 | fn broken_inner_item() { | ||
342 | do_check( | ||
343 | r" | ||
344 | fn foo() { | ||
345 | trait {} | ||
346 | <|> | ||
347 | } | ||
348 | ", | ||
349 | &[], | ||
350 | ); | ||
351 | } | ||
352 | |||
320 | fn do_check_local_name(ra_fixture: &str, expected_offset: u32) { | 353 | fn do_check_local_name(ra_fixture: &str, expected_offset: u32) { |
321 | let (db, position) = TestDB::with_position(ra_fixture); | 354 | let (db, position) = TestDB::with_position(ra_fixture); |
322 | let file_id = position.file_id; | 355 | let file_id = position.file_id; |
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index 53599e74a..282ade2a3 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs | |||
@@ -2,27 +2,19 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_expand::{ | 5 | use hir_expand::{name::Name, InFile}; |
6 | hygiene::Hygiene, | ||
7 | name::{name, AsName, Name}, | ||
8 | AstId, InFile, | ||
9 | }; | ||
10 | use ra_prof::profile; | 6 | use ra_prof::profile; |
11 | use ra_syntax::ast::{ | 7 | use ra_syntax::ast; |
12 | self, AssocItem, AstNode, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner, | ||
13 | VisibilityOwner, | ||
14 | }; | ||
15 | 8 | ||
16 | use crate::{ | 9 | use crate::{ |
17 | attr::Attrs, | 10 | attr::Attrs, |
18 | body::LowerCtx, | 11 | body::Expander, |
19 | db::DefDatabase, | 12 | db::DefDatabase, |
20 | path::{path, AssociatedTypeBinding, GenericArgs, Path}, | 13 | item_tree::{AssocItem, ItemTreeId, ModItem}, |
21 | src::HasSource, | 14 | type_ref::{TypeBound, TypeRef}, |
22 | type_ref::{Mutability, TypeBound, TypeRef}, | ||
23 | visibility::RawVisibility, | 15 | visibility::RawVisibility, |
24 | AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule, | 16 | AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, |
25 | ImplId, Intern, Lookup, StaticId, TraitId, TypeAliasId, TypeAliasLoc, | 17 | Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc, |
26 | }; | 18 | }; |
27 | 19 | ||
28 | #[derive(Debug, Clone, PartialEq, Eq)] | 20 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -41,82 +33,27 @@ pub struct FunctionData { | |||
41 | impl FunctionData { | 33 | impl FunctionData { |
42 | pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> { | 34 | pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> { |
43 | let loc = func.lookup(db); | 35 | let loc = func.lookup(db); |
44 | let src = loc.source(db); | 36 | let item_tree = db.item_tree(loc.id.file_id); |
45 | let ctx = LowerCtx::new(db, src.file_id); | 37 | let func = &item_tree[loc.id.value]; |
46 | let name = src.value.name().map(|n| n.as_name()).unwrap_or_else(Name::missing); | 38 | |
47 | let mut params = Vec::new(); | 39 | Arc::new(FunctionData { |
48 | let mut has_self_param = false; | 40 | name: func.name.clone(), |
49 | if let Some(param_list) = src.value.param_list() { | 41 | params: func.params.to_vec(), |
50 | if let Some(self_param) = param_list.self_param() { | 42 | ret_type: func.ret_type.clone(), |
51 | let self_type = if let Some(type_ref) = self_param.ascribed_type() { | 43 | attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(), |
52 | TypeRef::from_ast(&ctx, type_ref) | 44 | has_self_param: func.has_self_param, |
53 | } else { | 45 | is_unsafe: func.is_unsafe, |
54 | let self_type = TypeRef::Path(name![Self].into()); | 46 | visibility: item_tree[func.visibility].clone(), |
55 | match self_param.kind() { | 47 | }) |
56 | ast::SelfParamKind::Owned => self_type, | ||
57 | ast::SelfParamKind::Ref => { | ||
58 | TypeRef::Reference(Box::new(self_type), Mutability::Shared) | ||
59 | } | ||
60 | ast::SelfParamKind::MutRef => { | ||
61 | TypeRef::Reference(Box::new(self_type), Mutability::Mut) | ||
62 | } | ||
63 | } | ||
64 | }; | ||
65 | params.push(self_type); | ||
66 | has_self_param = true; | ||
67 | } | ||
68 | for param in param_list.params() { | ||
69 | let type_ref = TypeRef::from_ast_opt(&ctx, param.ascribed_type()); | ||
70 | params.push(type_ref); | ||
71 | } | ||
72 | } | ||
73 | let attrs = Attrs::new(&src.value, &Hygiene::new(db.upcast(), src.file_id)); | ||
74 | |||
75 | let ret_type = if let Some(type_ref) = src.value.ret_type().and_then(|rt| rt.type_ref()) { | ||
76 | TypeRef::from_ast(&ctx, type_ref) | ||
77 | } else { | ||
78 | TypeRef::unit() | ||
79 | }; | ||
80 | |||
81 | let ret_type = if src.value.async_token().is_some() { | ||
82 | let future_impl = desugar_future_path(ret_type); | ||
83 | let ty_bound = TypeBound::Path(future_impl); | ||
84 | TypeRef::ImplTrait(vec![ty_bound]) | ||
85 | } else { | ||
86 | ret_type | ||
87 | }; | ||
88 | |||
89 | let is_unsafe = src.value.unsafe_token().is_some(); | ||
90 | |||
91 | let vis_default = RawVisibility::default_for_container(loc.container); | ||
92 | let visibility = | ||
93 | RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility())); | ||
94 | |||
95 | let sig = | ||
96 | FunctionData { name, params, ret_type, has_self_param, is_unsafe, visibility, attrs }; | ||
97 | Arc::new(sig) | ||
98 | } | 48 | } |
99 | } | 49 | } |
100 | 50 | ||
101 | fn desugar_future_path(orig: TypeRef) -> Path { | ||
102 | let path = path![core::future::Future]; | ||
103 | let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); | ||
104 | let mut last = GenericArgs::empty(); | ||
105 | last.bindings.push(AssociatedTypeBinding { | ||
106 | name: name![Output], | ||
107 | type_ref: Some(orig), | ||
108 | bounds: Vec::new(), | ||
109 | }); | ||
110 | generic_args.push(Some(Arc::new(last))); | ||
111 | |||
112 | Path::from_known_path(path, generic_args) | ||
113 | } | ||
114 | |||
115 | #[derive(Debug, Clone, PartialEq, Eq)] | 51 | #[derive(Debug, Clone, PartialEq, Eq)] |
116 | pub struct TypeAliasData { | 52 | pub struct TypeAliasData { |
117 | pub name: Name, | 53 | pub name: Name, |
118 | pub type_ref: Option<TypeRef>, | 54 | pub type_ref: Option<TypeRef>, |
119 | pub visibility: RawVisibility, | 55 | pub visibility: RawVisibility, |
56 | /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl). | ||
120 | pub bounds: Vec<TypeBound>, | 57 | pub bounds: Vec<TypeBound>, |
121 | } | 58 | } |
122 | 59 | ||
@@ -126,22 +63,15 @@ impl TypeAliasData { | |||
126 | typ: TypeAliasId, | 63 | typ: TypeAliasId, |
127 | ) -> Arc<TypeAliasData> { | 64 | ) -> Arc<TypeAliasData> { |
128 | let loc = typ.lookup(db); | 65 | let loc = typ.lookup(db); |
129 | let node = loc.source(db); | 66 | let item_tree = db.item_tree(loc.id.file_id); |
130 | let name = node.value.name().map_or_else(Name::missing, |n| n.as_name()); | 67 | let typ = &item_tree[loc.id.value]; |
131 | let lower_ctx = LowerCtx::new(db, node.file_id); | 68 | |
132 | let type_ref = node.value.type_ref().map(|it| TypeRef::from_ast(&lower_ctx, it)); | 69 | Arc::new(TypeAliasData { |
133 | let vis_default = RawVisibility::default_for_container(loc.container); | 70 | name: typ.name.clone(), |
134 | let visibility = RawVisibility::from_ast_with_default( | 71 | type_ref: typ.type_ref.clone(), |
135 | db, | 72 | visibility: item_tree[typ.visibility].clone(), |
136 | vis_default, | 73 | bounds: typ.bounds.to_vec(), |
137 | node.as_ref().map(|n| n.visibility()), | 74 | }) |
138 | ); | ||
139 | let bounds = if let Some(bound_list) = node.value.type_bound_list() { | ||
140 | bound_list.bounds().map(|it| TypeBound::from_ast(&lower_ctx, it)).collect() | ||
141 | } else { | ||
142 | Vec::new() | ||
143 | }; | ||
144 | Arc::new(TypeAliasData { name, type_ref, visibility, bounds }) | ||
145 | } | 75 | } |
146 | } | 76 | } |
147 | 77 | ||
@@ -155,30 +85,24 @@ pub struct TraitData { | |||
155 | impl TraitData { | 85 | impl TraitData { |
156 | pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> { | 86 | pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> { |
157 | let tr_loc = tr.lookup(db); | 87 | let tr_loc = tr.lookup(db); |
158 | let src = tr_loc.source(db); | 88 | let item_tree = db.item_tree(tr_loc.id.file_id); |
159 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); | 89 | let tr_def = &item_tree[tr_loc.id.value]; |
160 | let auto = src.value.auto_token().is_some(); | 90 | let name = tr_def.name.clone(); |
91 | let auto = tr_def.auto; | ||
161 | let module_id = tr_loc.container.module(db); | 92 | let module_id = tr_loc.container.module(db); |
162 | |||
163 | let container = AssocContainerId::TraitId(tr); | 93 | let container = AssocContainerId::TraitId(tr); |
164 | let mut items = Vec::new(); | 94 | let mut expander = Expander::new(db, tr_loc.id.file_id, module_id); |
165 | 95 | ||
166 | if let Some(item_list) = src.value.item_list() { | 96 | let items = collect_items( |
167 | let mut expander = Expander::new(db, tr_loc.ast_id.file_id, module_id); | 97 | db, |
168 | items.extend(collect_items( | 98 | module_id, |
169 | db, | 99 | &mut expander, |
170 | &mut expander, | 100 | tr_def.items.iter().copied(), |
171 | item_list.assoc_items(), | 101 | tr_loc.id.file_id, |
172 | src.file_id, | 102 | container, |
173 | container, | 103 | 100, |
174 | )); | 104 | ); |
175 | items.extend(collect_items_in_macros( | 105 | |
176 | db, | ||
177 | &mut expander, | ||
178 | &src.with_value(item_list), | ||
179 | container, | ||
180 | )); | ||
181 | } | ||
182 | Arc::new(TraitData { name, items, auto }) | 106 | Arc::new(TraitData { name, items, auto }) |
183 | } | 107 | } |
184 | 108 | ||
@@ -209,33 +133,28 @@ impl ImplData { | |||
209 | pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> { | 133 | pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> { |
210 | let _p = profile("impl_data_query"); | 134 | let _p = profile("impl_data_query"); |
211 | let impl_loc = id.lookup(db); | 135 | let impl_loc = id.lookup(db); |
212 | let src = impl_loc.source(db); | ||
213 | let lower_ctx = LowerCtx::new(db, src.file_id); | ||
214 | 136 | ||
215 | let target_trait = src.value.target_trait().map(|it| TypeRef::from_ast(&lower_ctx, it)); | 137 | let item_tree = db.item_tree(impl_loc.id.file_id); |
216 | let target_type = TypeRef::from_ast_opt(&lower_ctx, src.value.target_type()); | 138 | let impl_def = &item_tree[impl_loc.id.value]; |
217 | let is_negative = src.value.excl_token().is_some(); | 139 | let target_trait = impl_def.target_trait.clone(); |
140 | let target_type = impl_def.target_type.clone(); | ||
141 | let is_negative = impl_def.is_negative; | ||
218 | let module_id = impl_loc.container.module(db); | 142 | let module_id = impl_loc.container.module(db); |
219 | let container = AssocContainerId::ImplId(id); | 143 | let container = AssocContainerId::ImplId(id); |
144 | let mut expander = Expander::new(db, impl_loc.id.file_id, module_id); | ||
220 | 145 | ||
221 | let mut items: Vec<AssocItemId> = Vec::new(); | 146 | let items = collect_items( |
222 | 147 | db, | |
223 | if let Some(item_list) = src.value.item_list() { | 148 | module_id, |
224 | let mut expander = Expander::new(db, impl_loc.ast_id.file_id, module_id); | 149 | &mut expander, |
225 | items.extend( | 150 | impl_def.items.iter().copied(), |
226 | collect_items(db, &mut expander, item_list.assoc_items(), src.file_id, container) | 151 | impl_loc.id.file_id, |
227 | .into_iter() | 152 | container, |
228 | .map(|(_, item)| item), | 153 | 100, |
229 | ); | 154 | ); |
230 | items.extend( | 155 | let items = items.into_iter().map(|(_, item)| item).collect(); |
231 | collect_items_in_macros(db, &mut expander, &src.with_value(item_list), container) | ||
232 | .into_iter() | ||
233 | .map(|(_, item)| item), | ||
234 | ); | ||
235 | } | ||
236 | 156 | ||
237 | let res = ImplData { target_trait, target_type, items, is_negative }; | 157 | Arc::new(ImplData { target_trait, target_type, items, is_negative }) |
238 | Arc::new(res) | ||
239 | } | 158 | } |
240 | } | 159 | } |
241 | 160 | ||
@@ -250,22 +169,14 @@ pub struct ConstData { | |||
250 | impl ConstData { | 169 | impl ConstData { |
251 | pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> { | 170 | pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> { |
252 | let loc = konst.lookup(db); | 171 | let loc = konst.lookup(db); |
253 | let node = loc.source(db); | 172 | let item_tree = db.item_tree(loc.id.file_id); |
254 | let vis_default = RawVisibility::default_for_container(loc.container); | 173 | let konst = &item_tree[loc.id.value]; |
255 | Arc::new(ConstData::new(db, vis_default, node)) | ||
256 | } | ||
257 | 174 | ||
258 | fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>( | 175 | Arc::new(ConstData { |
259 | db: &dyn DefDatabase, | 176 | name: konst.name.clone(), |
260 | vis_default: RawVisibility, | 177 | type_ref: konst.type_ref.clone(), |
261 | node: InFile<N>, | 178 | visibility: item_tree[konst.visibility].clone(), |
262 | ) -> ConstData { | 179 | }) |
263 | let ctx = LowerCtx::new(db, node.file_id); | ||
264 | let name = node.value.name().map(|n| n.as_name()); | ||
265 | let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type()); | ||
266 | let visibility = | ||
267 | RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility())); | ||
268 | ConstData { name, type_ref, visibility } | ||
269 | } | 180 | } |
270 | } | 181 | } |
271 | 182 | ||
@@ -279,44 +190,25 @@ pub struct StaticData { | |||
279 | 190 | ||
280 | impl StaticData { | 191 | impl StaticData { |
281 | pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> { | 192 | pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> { |
282 | let node = konst.lookup(db).source(db); | 193 | let node = konst.lookup(db); |
283 | let ctx = LowerCtx::new(db, node.file_id); | 194 | let item_tree = db.item_tree(node.id.file_id); |
284 | 195 | let statik = &item_tree[node.id.value]; | |
285 | let name = node.value.name().map(|n| n.as_name()); | 196 | |
286 | let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type()); | 197 | Arc::new(StaticData { |
287 | let mutable = node.value.mut_token().is_some(); | 198 | name: Some(statik.name.clone()), |
288 | let visibility = RawVisibility::from_ast_with_default( | 199 | type_ref: statik.type_ref.clone(), |
289 | db, | 200 | visibility: item_tree[statik.visibility].clone(), |
290 | RawVisibility::private(), | 201 | mutable: statik.mutable, |
291 | node.map(|n| n.visibility()), | 202 | }) |
292 | ); | ||
293 | |||
294 | Arc::new(StaticData { name, type_ref, visibility, mutable }) | ||
295 | } | ||
296 | } | ||
297 | |||
298 | fn collect_items_in_macros( | ||
299 | db: &dyn DefDatabase, | ||
300 | expander: &mut Expander, | ||
301 | impl_def: &InFile<ast::ItemList>, | ||
302 | container: AssocContainerId, | ||
303 | ) -> Vec<(Name, AssocItemId)> { | ||
304 | let mut res = Vec::new(); | ||
305 | |||
306 | // We set a limit to protect against infinite recursion | ||
307 | let limit = 100; | ||
308 | |||
309 | for m in impl_def.value.syntax().children().filter_map(ast::MacroCall::cast) { | ||
310 | res.extend(collect_items_in_macro(db, expander, m, container, limit)) | ||
311 | } | 203 | } |
312 | |||
313 | res | ||
314 | } | 204 | } |
315 | 205 | ||
316 | fn collect_items_in_macro( | 206 | fn collect_items( |
317 | db: &dyn DefDatabase, | 207 | db: &dyn DefDatabase, |
208 | module: ModuleId, | ||
318 | expander: &mut Expander, | 209 | expander: &mut Expander, |
319 | m: ast::MacroCall, | 210 | assoc_items: impl Iterator<Item = AssocItem>, |
211 | file_id: crate::HirFileId, | ||
320 | container: AssocContainerId, | 212 | container: AssocContainerId, |
321 | limit: usize, | 213 | limit: usize, |
322 | ) -> Vec<(Name, AssocItemId)> { | 214 | ) -> Vec<(Name, AssocItemId)> { |
@@ -324,62 +216,62 @@ fn collect_items_in_macro( | |||
324 | return Vec::new(); | 216 | return Vec::new(); |
325 | } | 217 | } |
326 | 218 | ||
327 | if let Some((mark, items)) = expander.enter_expand(db, None, m) { | 219 | let item_tree = db.item_tree(file_id); |
328 | let items: InFile<ast::MacroItems> = expander.to_source(items); | 220 | let cfg_options = db.crate_graph()[module.krate].cfg_options.clone(); |
329 | let mut res = collect_items( | 221 | |
330 | db, | 222 | let mut items = Vec::new(); |
331 | expander, | 223 | for item in assoc_items { |
332 | items.value.items().filter_map(|it| AssocItem::cast(it.syntax().clone())), | 224 | match item { |
333 | items.file_id, | 225 | AssocItem::Function(id) => { |
334 | container, | 226 | let item = &item_tree[id]; |
335 | ); | 227 | let attrs = item_tree.attrs(ModItem::from(id).into()); |
336 | 228 | if !attrs.is_cfg_enabled(&cfg_options) { | |
337 | // Recursive collect macros | 229 | continue; |
338 | // Note that ast::ModuleItem do not include ast::MacroCall | ||
339 | // We cannot use ModuleItemOwner::items here | ||
340 | for it in items.value.syntax().children().filter_map(ast::MacroCall::cast) { | ||
341 | res.extend(collect_items_in_macro(db, expander, it, container, limit - 1)) | ||
342 | } | ||
343 | expander.exit(db, mark); | ||
344 | res | ||
345 | } else { | ||
346 | Vec::new() | ||
347 | } | ||
348 | } | ||
349 | |||
350 | fn collect_items( | ||
351 | db: &dyn DefDatabase, | ||
352 | expander: &mut Expander, | ||
353 | assoc_items: impl Iterator<Item = AssocItem>, | ||
354 | file_id: crate::HirFileId, | ||
355 | container: AssocContainerId, | ||
356 | ) -> Vec<(Name, AssocItemId)> { | ||
357 | let items = db.ast_id_map(file_id); | ||
358 | |||
359 | assoc_items | ||
360 | .filter_map(|item_node| match item_node { | ||
361 | ast::AssocItem::FnDef(it) => { | ||
362 | let name = it.name().map_or_else(Name::missing, |it| it.as_name()); | ||
363 | if !expander.is_cfg_enabled(&it) { | ||
364 | return None; | ||
365 | } | 230 | } |
366 | let def = FunctionLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } | 231 | let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db); |
367 | .intern(db); | 232 | items.push((item.name.clone(), def.into())); |
368 | Some((name, def.into())) | ||
369 | } | 233 | } |
370 | ast::AssocItem::ConstDef(it) => { | 234 | // FIXME: cfg? |
371 | let name = it.name().map_or_else(Name::missing, |it| it.as_name()); | 235 | AssocItem::Const(id) => { |
372 | let def = ConstLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } | 236 | let item = &item_tree[id]; |
373 | .intern(db); | 237 | let name = match item.name.clone() { |
374 | Some((name, def.into())) | 238 | Some(name) => name, |
239 | None => continue, | ||
240 | }; | ||
241 | let def = ConstLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db); | ||
242 | items.push((name, def.into())); | ||
375 | } | 243 | } |
376 | ast::AssocItem::TypeAliasDef(it) => { | 244 | AssocItem::TypeAlias(id) => { |
377 | let name = it.name().map_or_else(Name::missing, |it| it.as_name()); | 245 | let item = &item_tree[id]; |
378 | let def = | 246 | let def = TypeAliasLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db); |
379 | TypeAliasLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } | 247 | items.push((item.name.clone(), def.into())); |
380 | .intern(db); | ||
381 | Some((name, def.into())) | ||
382 | } | 248 | } |
383 | }) | 249 | AssocItem::MacroCall(call) => { |
384 | .collect() | 250 | let call = &item_tree[call]; |
251 | let ast_id_map = db.ast_id_map(file_id); | ||
252 | let root = db.parse_or_expand(file_id).unwrap(); | ||
253 | let call = ast_id_map.get(call.ast_id).to_node(&root); | ||
254 | |||
255 | if let Some((mark, mac)) = expander.enter_expand(db, None, call) { | ||
256 | let src: InFile<ast::MacroItems> = expander.to_source(mac); | ||
257 | let item_tree = db.item_tree(src.file_id); | ||
258 | let iter = | ||
259 | item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item); | ||
260 | items.extend(collect_items( | ||
261 | db, | ||
262 | module, | ||
263 | expander, | ||
264 | iter, | ||
265 | src.file_id, | ||
266 | container, | ||
267 | limit - 1, | ||
268 | )); | ||
269 | |||
270 | expander.exit(db, mark); | ||
271 | } | ||
272 | } | ||
273 | } | ||
274 | } | ||
275 | |||
276 | items | ||
385 | } | 277 | } |
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index 10cc26480..9c3ede2d7 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs | |||
@@ -14,8 +14,9 @@ use crate::{ | |||
14 | docs::Documentation, | 14 | docs::Documentation, |
15 | generics::GenericParams, | 15 | generics::GenericParams, |
16 | import_map::ImportMap, | 16 | import_map::ImportMap, |
17 | item_tree::ItemTree, | ||
17 | lang_item::{LangItemTarget, LangItems}, | 18 | lang_item::{LangItemTarget, LangItems}, |
18 | nameres::{raw::RawItems, CrateDefMap}, | 19 | nameres::CrateDefMap, |
19 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, | 20 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, |
20 | GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, | 21 | GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, |
21 | TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, | 22 | TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, |
@@ -45,8 +46,8 @@ pub trait InternDatabase: SourceDatabase { | |||
45 | 46 | ||
46 | #[salsa::query_group(DefDatabaseStorage)] | 47 | #[salsa::query_group(DefDatabaseStorage)] |
47 | pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | 48 | pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { |
48 | #[salsa::invoke(RawItems::raw_items_query)] | 49 | #[salsa::invoke(ItemTree::item_tree_query)] |
49 | fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>; | 50 | fn item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; |
50 | 51 | ||
51 | #[salsa::invoke(crate_def_map_wait)] | 52 | #[salsa::invoke(crate_def_map_wait)] |
52 | #[salsa::transparent] | 53 | #[salsa::transparent] |
diff --git a/crates/ra_hir_def/src/generics.rs b/crates/ra_hir_def/src/generics.rs index 09a5241f7..6a0f493a7 100644 --- a/crates/ra_hir_def/src/generics.rs +++ b/crates/ra_hir_def/src/generics.rs | |||
@@ -42,7 +42,7 @@ pub enum TypeParamProvenance { | |||
42 | } | 42 | } |
43 | 43 | ||
44 | /// Data about the generic parameters of a function, struct, impl, etc. | 44 | /// Data about the generic parameters of a function, struct, impl, etc. |
45 | #[derive(Clone, PartialEq, Eq, Debug)] | 45 | #[derive(Clone, PartialEq, Eq, Debug, Default)] |
46 | pub struct GenericParams { | 46 | pub struct GenericParams { |
47 | pub types: Arena<TypeParamData>, | 47 | pub types: Arena<TypeParamData>, |
48 | // lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>, | 48 | // lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>, |
@@ -74,8 +74,53 @@ impl GenericParams { | |||
74 | def: GenericDefId, | 74 | def: GenericDefId, |
75 | ) -> Arc<GenericParams> { | 75 | ) -> Arc<GenericParams> { |
76 | let _p = profile("generic_params_query"); | 76 | let _p = profile("generic_params_query"); |
77 | let (params, _source_map) = GenericParams::new(db, def); | 77 | |
78 | Arc::new(params) | 78 | let generics = match def { |
79 | GenericDefId::FunctionId(id) => { | ||
80 | let id = id.lookup(db).id; | ||
81 | let tree = db.item_tree(id.file_id); | ||
82 | let item = &tree[id.value]; | ||
83 | tree[item.generic_params].clone() | ||
84 | } | ||
85 | GenericDefId::AdtId(AdtId::StructId(id)) => { | ||
86 | let id = id.lookup(db).id; | ||
87 | let tree = db.item_tree(id.file_id); | ||
88 | let item = &tree[id.value]; | ||
89 | tree[item.generic_params].clone() | ||
90 | } | ||
91 | GenericDefId::AdtId(AdtId::EnumId(id)) => { | ||
92 | let id = id.lookup(db).id; | ||
93 | let tree = db.item_tree(id.file_id); | ||
94 | let item = &tree[id.value]; | ||
95 | tree[item.generic_params].clone() | ||
96 | } | ||
97 | GenericDefId::AdtId(AdtId::UnionId(id)) => { | ||
98 | let id = id.lookup(db).id; | ||
99 | let tree = db.item_tree(id.file_id); | ||
100 | let item = &tree[id.value]; | ||
101 | tree[item.generic_params].clone() | ||
102 | } | ||
103 | GenericDefId::TraitId(id) => { | ||
104 | let id = id.lookup(db).id; | ||
105 | let tree = db.item_tree(id.file_id); | ||
106 | let item = &tree[id.value]; | ||
107 | tree[item.generic_params].clone() | ||
108 | } | ||
109 | GenericDefId::TypeAliasId(id) => { | ||
110 | let id = id.lookup(db).id; | ||
111 | let tree = db.item_tree(id.file_id); | ||
112 | let item = &tree[id.value]; | ||
113 | tree[item.generic_params].clone() | ||
114 | } | ||
115 | GenericDefId::ImplId(id) => { | ||
116 | let id = id.lookup(db).id; | ||
117 | let tree = db.item_tree(id.file_id); | ||
118 | let item = &tree[id.value]; | ||
119 | tree[item.generic_params].clone() | ||
120 | } | ||
121 | GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => GenericParams::default(), | ||
122 | }; | ||
123 | Arc::new(generics) | ||
79 | } | 124 | } |
80 | 125 | ||
81 | fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) { | 126 | fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) { |
@@ -156,7 +201,12 @@ impl GenericParams { | |||
156 | (generics, InFile::new(file_id, sm)) | 201 | (generics, InFile::new(file_id, sm)) |
157 | } | 202 | } |
158 | 203 | ||
159 | fn fill(&mut self, lower_ctx: &LowerCtx, sm: &mut SourceMap, node: &dyn TypeParamsOwner) { | 204 | pub(crate) fn fill( |
205 | &mut self, | ||
206 | lower_ctx: &LowerCtx, | ||
207 | sm: &mut SourceMap, | ||
208 | node: &dyn TypeParamsOwner, | ||
209 | ) { | ||
160 | if let Some(params) = node.type_param_list() { | 210 | if let Some(params) = node.type_param_list() { |
161 | self.fill_params(lower_ctx, sm, params) | 211 | self.fill_params(lower_ctx, sm, params) |
162 | } | 212 | } |
@@ -165,7 +215,7 @@ impl GenericParams { | |||
165 | } | 215 | } |
166 | } | 216 | } |
167 | 217 | ||
168 | fn fill_bounds( | 218 | pub(crate) fn fill_bounds( |
169 | &mut self, | 219 | &mut self, |
170 | lower_ctx: &LowerCtx, | 220 | lower_ctx: &LowerCtx, |
171 | node: &dyn ast::TypeBoundsOwner, | 221 | node: &dyn ast::TypeBoundsOwner, |
@@ -229,7 +279,7 @@ impl GenericParams { | |||
229 | .push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound }); | 279 | .push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound }); |
230 | } | 280 | } |
231 | 281 | ||
232 | fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) { | 282 | pub(crate) fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) { |
233 | type_ref.walk(&mut |type_ref| { | 283 | type_ref.walk(&mut |type_ref| { |
234 | if let TypeRef::ImplTrait(bounds) = type_ref { | 284 | if let TypeRef::ImplTrait(bounds) = type_ref { |
235 | let param = TypeParamData { | 285 | let param = TypeParamData { |
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index b03ba939a..c81b966c3 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -5,6 +5,7 @@ use hir_expand::name::Name; | |||
5 | use once_cell::sync::Lazy; | 5 | use once_cell::sync::Lazy; |
6 | use ra_db::CrateId; | 6 | use ra_db::CrateId; |
7 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
8 | use test_utils::mark; | ||
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, | 11 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, |
@@ -126,19 +127,27 @@ impl ItemScope { | |||
126 | let mut changed = false; | 127 | let mut changed = false; |
127 | let existing = self.visible.entry(name).or_default(); | 128 | let existing = self.visible.entry(name).or_default(); |
128 | 129 | ||
129 | if existing.types.is_none() && def.types.is_some() { | 130 | macro_rules! check_changed { |
130 | existing.types = def.types; | 131 | ($changed:ident, $existing:expr, $def:expr) => { |
131 | changed = true; | 132 | match ($existing, $def) { |
132 | } | 133 | (None, Some(_)) => { |
133 | if existing.values.is_none() && def.values.is_some() { | 134 | $existing = $def; |
134 | existing.values = def.values; | 135 | $changed = true; |
135 | changed = true; | 136 | } |
136 | } | 137 | (Some(e), Some(d)) if e.0 != d.0 => { |
137 | if existing.macros.is_none() && def.macros.is_some() { | 138 | mark::hit!(import_shadowed); |
138 | existing.macros = def.macros; | 139 | $existing = $def; |
139 | changed = true; | 140 | $changed = true; |
141 | } | ||
142 | _ => {} | ||
143 | } | ||
144 | }; | ||
140 | } | 145 | } |
141 | 146 | ||
147 | check_changed!(changed, existing.types, def.types); | ||
148 | check_changed!(changed, existing.values, def.values); | ||
149 | check_changed!(changed, existing.macros, def.macros); | ||
150 | |||
142 | changed | 151 | changed |
143 | } | 152 | } |
144 | 153 | ||
diff --git a/crates/ra_hir_def/src/item_tree.rs b/crates/ra_hir_def/src/item_tree.rs new file mode 100644 index 000000000..3e603bd55 --- /dev/null +++ b/crates/ra_hir_def/src/item_tree.rs | |||
@@ -0,0 +1,753 @@ | |||
1 | //! A simplified AST that only contains items. | ||
2 | |||
3 | mod lower; | ||
4 | #[cfg(test)] | ||
5 | mod tests; | ||
6 | |||
7 | use std::{ | ||
8 | any::type_name, | ||
9 | fmt::{self, Debug}, | ||
10 | hash::{Hash, Hasher}, | ||
11 | marker::PhantomData, | ||
12 | ops::{Index, Range}, | ||
13 | sync::Arc, | ||
14 | }; | ||
15 | |||
16 | use ast::{AstNode, AttrsOwner, NameOwner, StructKind, TypeAscriptionOwner}; | ||
17 | use either::Either; | ||
18 | use hir_expand::{ | ||
19 | ast_id_map::FileAstId, | ||
20 | hygiene::Hygiene, | ||
21 | name::{name, AsName, Name}, | ||
22 | HirFileId, InFile, | ||
23 | }; | ||
24 | use ra_arena::{Arena, Idx, RawId}; | ||
25 | use ra_syntax::{ast, match_ast}; | ||
26 | use rustc_hash::FxHashMap; | ||
27 | use smallvec::SmallVec; | ||
28 | use test_utils::mark; | ||
29 | |||
30 | use crate::{ | ||
31 | attr::Attrs, | ||
32 | db::DefDatabase, | ||
33 | generics::GenericParams, | ||
34 | path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind}, | ||
35 | type_ref::{Mutability, TypeBound, TypeRef}, | ||
36 | visibility::RawVisibility, | ||
37 | }; | ||
38 | |||
39 | #[derive(Copy, Clone, Eq, PartialEq)] | ||
40 | pub struct RawVisibilityId(u32); | ||
41 | |||
42 | impl RawVisibilityId { | ||
43 | pub const PUB: Self = RawVisibilityId(u32::max_value()); | ||
44 | pub const PRIV: Self = RawVisibilityId(u32::max_value() - 1); | ||
45 | pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 2); | ||
46 | } | ||
47 | |||
48 | impl fmt::Debug for RawVisibilityId { | ||
49 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
50 | let mut f = f.debug_tuple("RawVisibilityId"); | ||
51 | match *self { | ||
52 | Self::PUB => f.field(&"pub"), | ||
53 | Self::PRIV => f.field(&"pub(self)"), | ||
54 | Self::PUB_CRATE => f.field(&"pub(crate)"), | ||
55 | _ => f.field(&self.0), | ||
56 | }; | ||
57 | f.finish() | ||
58 | } | ||
59 | } | ||
60 | |||
61 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
62 | pub struct GenericParamsId(u32); | ||
63 | |||
64 | impl GenericParamsId { | ||
65 | pub const EMPTY: Self = GenericParamsId(u32::max_value()); | ||
66 | } | ||
67 | |||
68 | /// The item tree of a source file. | ||
69 | #[derive(Debug, Eq, PartialEq)] | ||
70 | pub struct ItemTree { | ||
71 | top_level: SmallVec<[ModItem; 1]>, | ||
72 | attrs: FxHashMap<AttrOwner, Attrs>, | ||
73 | inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>, | ||
74 | |||
75 | data: Option<Box<ItemTreeData>>, | ||
76 | } | ||
77 | |||
78 | impl ItemTree { | ||
79 | pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { | ||
80 | let _p = ra_prof::profile("item_tree_query").detail(|| format!("{:?}", file_id)); | ||
81 | let syntax = if let Some(node) = db.parse_or_expand(file_id) { | ||
82 | node | ||
83 | } else { | ||
84 | return Arc::new(Self::empty()); | ||
85 | }; | ||
86 | |||
87 | let hygiene = Hygiene::new(db.upcast(), file_id); | ||
88 | let ctx = lower::Ctx::new(db, hygiene.clone(), file_id); | ||
89 | let mut top_attrs = None; | ||
90 | let mut item_tree = match_ast! { | ||
91 | match syntax { | ||
92 | ast::SourceFile(file) => { | ||
93 | top_attrs = Some(Attrs::new(&file, &hygiene)); | ||
94 | ctx.lower_module_items(&file) | ||
95 | }, | ||
96 | ast::MacroItems(items) => { | ||
97 | ctx.lower_module_items(&items) | ||
98 | }, | ||
99 | // Macros can expand to expressions. We return an empty item tree in this case, but | ||
100 | // still need to collect inner items. | ||
101 | ast::Expr(e) => { | ||
102 | ctx.lower_inner_items(e.syntax()) | ||
103 | }, | ||
104 | _ => { | ||
105 | panic!("cannot create item tree from {:?}", syntax); | ||
106 | }, | ||
107 | } | ||
108 | }; | ||
109 | |||
110 | if let Some(attrs) = top_attrs { | ||
111 | item_tree.attrs.insert(AttrOwner::TopLevel, attrs); | ||
112 | } | ||
113 | item_tree.shrink_to_fit(); | ||
114 | Arc::new(item_tree) | ||
115 | } | ||
116 | |||
117 | fn empty() -> Self { | ||
118 | Self { | ||
119 | top_level: Default::default(), | ||
120 | attrs: Default::default(), | ||
121 | inner_items: Default::default(), | ||
122 | data: Default::default(), | ||
123 | } | ||
124 | } | ||
125 | |||
126 | fn shrink_to_fit(&mut self) { | ||
127 | if let Some(data) = &mut self.data { | ||
128 | let ItemTreeData { | ||
129 | imports, | ||
130 | extern_crates, | ||
131 | functions, | ||
132 | structs, | ||
133 | fields, | ||
134 | unions, | ||
135 | enums, | ||
136 | variants, | ||
137 | consts, | ||
138 | statics, | ||
139 | traits, | ||
140 | impls, | ||
141 | type_aliases, | ||
142 | mods, | ||
143 | macro_calls, | ||
144 | exprs, | ||
145 | vis, | ||
146 | generics, | ||
147 | } = &mut **data; | ||
148 | |||
149 | imports.shrink_to_fit(); | ||
150 | extern_crates.shrink_to_fit(); | ||
151 | functions.shrink_to_fit(); | ||
152 | structs.shrink_to_fit(); | ||
153 | fields.shrink_to_fit(); | ||
154 | unions.shrink_to_fit(); | ||
155 | enums.shrink_to_fit(); | ||
156 | variants.shrink_to_fit(); | ||
157 | consts.shrink_to_fit(); | ||
158 | statics.shrink_to_fit(); | ||
159 | traits.shrink_to_fit(); | ||
160 | impls.shrink_to_fit(); | ||
161 | type_aliases.shrink_to_fit(); | ||
162 | mods.shrink_to_fit(); | ||
163 | macro_calls.shrink_to_fit(); | ||
164 | exprs.shrink_to_fit(); | ||
165 | |||
166 | vis.arena.shrink_to_fit(); | ||
167 | generics.arena.shrink_to_fit(); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | /// Returns an iterator over all items located at the top level of the `HirFileId` this | ||
172 | /// `ItemTree` was created from. | ||
173 | pub fn top_level_items(&self) -> &[ModItem] { | ||
174 | &self.top_level | ||
175 | } | ||
176 | |||
177 | /// Returns the inner attributes of the source file. | ||
178 | pub fn top_level_attrs(&self) -> &Attrs { | ||
179 | self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&Attrs::EMPTY) | ||
180 | } | ||
181 | |||
182 | pub fn attrs(&self, of: AttrOwner) -> &Attrs { | ||
183 | self.attrs.get(&of).unwrap_or(&Attrs::EMPTY) | ||
184 | } | ||
185 | |||
186 | /// Returns the lowered inner items that `ast` corresponds to. | ||
187 | /// | ||
188 | /// Most AST items are lowered to a single `ModItem`, but some (eg. `use` items) may be lowered | ||
189 | /// to multiple items in the `ItemTree`. | ||
190 | pub fn inner_items(&self, ast: FileAstId<ast::ModuleItem>) -> &[ModItem] { | ||
191 | &self.inner_items[&ast] | ||
192 | } | ||
193 | |||
194 | pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ { | ||
195 | self.inner_items.values().flatten().copied() | ||
196 | } | ||
197 | |||
198 | pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source { | ||
199 | // This unwrap cannot fail, since it has either succeeded above, or resulted in an empty | ||
200 | // ItemTree (in which case there is no valid `FileItemTreeId` to call this method with). | ||
201 | let root = | ||
202 | db.parse_or_expand(of.file_id).expect("parse_or_expand failed on constructed ItemTree"); | ||
203 | |||
204 | let id = self[of.value].ast_id(); | ||
205 | let map = db.ast_id_map(of.file_id); | ||
206 | let ptr = map.get(id); | ||
207 | ptr.to_node(&root) | ||
208 | } | ||
209 | |||
210 | fn data(&self) -> &ItemTreeData { | ||
211 | self.data.as_ref().expect("attempted to access data of empty ItemTree") | ||
212 | } | ||
213 | |||
214 | fn data_mut(&mut self) -> &mut ItemTreeData { | ||
215 | self.data.get_or_insert_with(Box::default) | ||
216 | } | ||
217 | } | ||
218 | |||
219 | #[derive(Default, Debug, Eq, PartialEq)] | ||
220 | struct ItemVisibilities { | ||
221 | arena: Arena<RawVisibility>, | ||
222 | } | ||
223 | |||
224 | impl ItemVisibilities { | ||
225 | fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId { | ||
226 | match &vis { | ||
227 | RawVisibility::Public => RawVisibilityId::PUB, | ||
228 | RawVisibility::Module(path) if path.segments.is_empty() => match &path.kind { | ||
229 | PathKind::Super(0) => RawVisibilityId::PRIV, | ||
230 | PathKind::Crate => RawVisibilityId::PUB_CRATE, | ||
231 | _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()), | ||
232 | }, | ||
233 | _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()), | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | |||
238 | static VIS_PUB: RawVisibility = RawVisibility::Public; | ||
239 | static VIS_PRIV: RawVisibility = | ||
240 | RawVisibility::Module(ModPath { kind: PathKind::Super(0), segments: Vec::new() }); | ||
241 | static VIS_PUB_CRATE: RawVisibility = | ||
242 | RawVisibility::Module(ModPath { kind: PathKind::Crate, segments: Vec::new() }); | ||
243 | |||
244 | #[derive(Default, Debug, Eq, PartialEq)] | ||
245 | struct GenericParamsStorage { | ||
246 | arena: Arena<GenericParams>, | ||
247 | } | ||
248 | |||
249 | impl GenericParamsStorage { | ||
250 | fn alloc(&mut self, params: GenericParams) -> GenericParamsId { | ||
251 | if params.types.is_empty() && params.where_predicates.is_empty() { | ||
252 | return GenericParamsId::EMPTY; | ||
253 | } | ||
254 | |||
255 | GenericParamsId(self.arena.alloc(params).into_raw().into()) | ||
256 | } | ||
257 | } | ||
258 | |||
259 | static EMPTY_GENERICS: GenericParams = | ||
260 | GenericParams { types: Arena::new(), where_predicates: Vec::new() }; | ||
261 | |||
262 | #[derive(Default, Debug, Eq, PartialEq)] | ||
263 | struct ItemTreeData { | ||
264 | imports: Arena<Import>, | ||
265 | extern_crates: Arena<ExternCrate>, | ||
266 | functions: Arena<Function>, | ||
267 | structs: Arena<Struct>, | ||
268 | fields: Arena<Field>, | ||
269 | unions: Arena<Union>, | ||
270 | enums: Arena<Enum>, | ||
271 | variants: Arena<Variant>, | ||
272 | consts: Arena<Const>, | ||
273 | statics: Arena<Static>, | ||
274 | traits: Arena<Trait>, | ||
275 | impls: Arena<Impl>, | ||
276 | type_aliases: Arena<TypeAlias>, | ||
277 | mods: Arena<Mod>, | ||
278 | macro_calls: Arena<MacroCall>, | ||
279 | exprs: Arena<Expr>, | ||
280 | |||
281 | vis: ItemVisibilities, | ||
282 | generics: GenericParamsStorage, | ||
283 | } | ||
284 | |||
285 | #[derive(Debug, Eq, PartialEq, Hash)] | ||
286 | pub enum AttrOwner { | ||
287 | /// Attributes on an item. | ||
288 | ModItem(ModItem), | ||
289 | /// Inner attributes of the source file. | ||
290 | TopLevel, | ||
291 | |||
292 | Variant(Idx<Variant>), | ||
293 | Field(Idx<Field>), | ||
294 | // FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`. | ||
295 | } | ||
296 | |||
297 | macro_rules! from_attrs { | ||
298 | ( $( $var:ident($t:ty) ),+ ) => { | ||
299 | $( | ||
300 | impl From<$t> for AttrOwner { | ||
301 | fn from(t: $t) -> AttrOwner { | ||
302 | AttrOwner::$var(t) | ||
303 | } | ||
304 | } | ||
305 | )+ | ||
306 | }; | ||
307 | } | ||
308 | |||
309 | from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>)); | ||
310 | |||
311 | /// Trait implemented by all item nodes in the item tree. | ||
312 | pub trait ItemTreeNode: Clone { | ||
313 | type Source: AstNode + Into<ast::ModuleItem>; | ||
314 | |||
315 | fn ast_id(&self) -> FileAstId<Self::Source>; | ||
316 | |||
317 | /// Looks up an instance of `Self` in an item tree. | ||
318 | fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self; | ||
319 | |||
320 | /// Downcasts a `ModItem` to a `FileItemTreeId` specific to this type. | ||
321 | fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>>; | ||
322 | |||
323 | /// Upcasts a `FileItemTreeId` to a generic `ModItem`. | ||
324 | fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem; | ||
325 | } | ||
326 | |||
327 | pub struct FileItemTreeId<N: ItemTreeNode> { | ||
328 | index: Idx<N>, | ||
329 | _p: PhantomData<N>, | ||
330 | } | ||
331 | |||
332 | impl<N: ItemTreeNode> Clone for FileItemTreeId<N> { | ||
333 | fn clone(&self) -> Self { | ||
334 | Self { index: self.index, _p: PhantomData } | ||
335 | } | ||
336 | } | ||
337 | impl<N: ItemTreeNode> Copy for FileItemTreeId<N> {} | ||
338 | |||
339 | impl<N: ItemTreeNode> PartialEq for FileItemTreeId<N> { | ||
340 | fn eq(&self, other: &FileItemTreeId<N>) -> bool { | ||
341 | self.index == other.index | ||
342 | } | ||
343 | } | ||
344 | impl<N: ItemTreeNode> Eq for FileItemTreeId<N> {} | ||
345 | |||
346 | impl<N: ItemTreeNode> Hash for FileItemTreeId<N> { | ||
347 | fn hash<H: Hasher>(&self, state: &mut H) { | ||
348 | self.index.hash(state) | ||
349 | } | ||
350 | } | ||
351 | |||
352 | impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> { | ||
353 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
354 | self.index.fmt(f) | ||
355 | } | ||
356 | } | ||
357 | |||
358 | pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>; | ||
359 | |||
360 | macro_rules! mod_items { | ||
361 | ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => { | ||
362 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] | ||
363 | pub enum ModItem { | ||
364 | $( | ||
365 | $typ(FileItemTreeId<$typ>), | ||
366 | )+ | ||
367 | } | ||
368 | |||
369 | $( | ||
370 | impl From<FileItemTreeId<$typ>> for ModItem { | ||
371 | fn from(id: FileItemTreeId<$typ>) -> ModItem { | ||
372 | ModItem::$typ(id) | ||
373 | } | ||
374 | } | ||
375 | )+ | ||
376 | |||
377 | $( | ||
378 | impl ItemTreeNode for $typ { | ||
379 | type Source = $ast; | ||
380 | |||
381 | fn ast_id(&self) -> FileAstId<Self::Source> { | ||
382 | self.ast_id | ||
383 | } | ||
384 | |||
385 | fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self { | ||
386 | &tree.data().$fld[index] | ||
387 | } | ||
388 | |||
389 | fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> { | ||
390 | if let ModItem::$typ(id) = mod_item { | ||
391 | Some(id) | ||
392 | } else { | ||
393 | None | ||
394 | } | ||
395 | } | ||
396 | |||
397 | fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem { | ||
398 | ModItem::$typ(id) | ||
399 | } | ||
400 | } | ||
401 | |||
402 | impl Index<Idx<$typ>> for ItemTree { | ||
403 | type Output = $typ; | ||
404 | |||
405 | fn index(&self, index: Idx<$typ>) -> &Self::Output { | ||
406 | &self.data().$fld[index] | ||
407 | } | ||
408 | } | ||
409 | )+ | ||
410 | }; | ||
411 | } | ||
412 | |||
413 | mod_items! { | ||
414 | Import in imports -> ast::UseItem, | ||
415 | ExternCrate in extern_crates -> ast::ExternCrateItem, | ||
416 | Function in functions -> ast::FnDef, | ||
417 | Struct in structs -> ast::StructDef, | ||
418 | Union in unions -> ast::UnionDef, | ||
419 | Enum in enums -> ast::EnumDef, | ||
420 | Const in consts -> ast::ConstDef, | ||
421 | Static in statics -> ast::StaticDef, | ||
422 | Trait in traits -> ast::TraitDef, | ||
423 | Impl in impls -> ast::ImplDef, | ||
424 | TypeAlias in type_aliases -> ast::TypeAliasDef, | ||
425 | Mod in mods -> ast::Module, | ||
426 | MacroCall in macro_calls -> ast::MacroCall, | ||
427 | } | ||
428 | |||
429 | macro_rules! impl_index { | ||
430 | ( $($fld:ident: $t:ty),+ $(,)? ) => { | ||
431 | $( | ||
432 | impl Index<Idx<$t>> for ItemTree { | ||
433 | type Output = $t; | ||
434 | |||
435 | fn index(&self, index: Idx<$t>) -> &Self::Output { | ||
436 | &self.data().$fld[index] | ||
437 | } | ||
438 | } | ||
439 | )+ | ||
440 | }; | ||
441 | } | ||
442 | |||
443 | impl_index!(fields: Field, variants: Variant, exprs: Expr); | ||
444 | |||
445 | impl Index<RawVisibilityId> for ItemTree { | ||
446 | type Output = RawVisibility; | ||
447 | fn index(&self, index: RawVisibilityId) -> &Self::Output { | ||
448 | match index { | ||
449 | RawVisibilityId::PRIV => &VIS_PRIV, | ||
450 | RawVisibilityId::PUB => &VIS_PUB, | ||
451 | RawVisibilityId::PUB_CRATE => &VIS_PUB_CRATE, | ||
452 | _ => &self.data().vis.arena[Idx::from_raw(index.0.into())], | ||
453 | } | ||
454 | } | ||
455 | } | ||
456 | |||
457 | impl Index<GenericParamsId> for ItemTree { | ||
458 | type Output = GenericParams; | ||
459 | |||
460 | fn index(&self, index: GenericParamsId) -> &Self::Output { | ||
461 | match index { | ||
462 | GenericParamsId::EMPTY => &EMPTY_GENERICS, | ||
463 | _ => &self.data().generics.arena[Idx::from_raw(index.0.into())], | ||
464 | } | ||
465 | } | ||
466 | } | ||
467 | |||
468 | impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree { | ||
469 | type Output = N; | ||
470 | fn index(&self, id: FileItemTreeId<N>) -> &N { | ||
471 | N::lookup(self, id.index) | ||
472 | } | ||
473 | } | ||
474 | |||
475 | /// A desugared `use` import. | ||
476 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
477 | pub struct Import { | ||
478 | pub path: ModPath, | ||
479 | pub alias: Option<ImportAlias>, | ||
480 | pub visibility: RawVisibilityId, | ||
481 | pub is_glob: bool, | ||
482 | pub is_prelude: bool, | ||
483 | /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many | ||
484 | /// `Import`s can map to the same `use` item. | ||
485 | pub ast_id: FileAstId<ast::UseItem>, | ||
486 | } | ||
487 | |||
488 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
489 | pub struct ExternCrate { | ||
490 | pub path: ModPath, | ||
491 | pub alias: Option<ImportAlias>, | ||
492 | pub visibility: RawVisibilityId, | ||
493 | /// Whether this is a `#[macro_use] extern crate ...`. | ||
494 | pub is_macro_use: bool, | ||
495 | pub ast_id: FileAstId<ast::ExternCrateItem>, | ||
496 | } | ||
497 | |||
498 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
499 | pub struct Function { | ||
500 | pub name: Name, | ||
501 | pub visibility: RawVisibilityId, | ||
502 | pub generic_params: GenericParamsId, | ||
503 | pub has_self_param: bool, | ||
504 | pub is_unsafe: bool, | ||
505 | pub params: Box<[TypeRef]>, | ||
506 | pub ret_type: TypeRef, | ||
507 | pub ast_id: FileAstId<ast::FnDef>, | ||
508 | } | ||
509 | |||
510 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
511 | pub struct Struct { | ||
512 | pub name: Name, | ||
513 | pub visibility: RawVisibilityId, | ||
514 | pub generic_params: GenericParamsId, | ||
515 | pub fields: Fields, | ||
516 | pub ast_id: FileAstId<ast::StructDef>, | ||
517 | pub kind: StructDefKind, | ||
518 | } | ||
519 | |||
520 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
521 | pub enum StructDefKind { | ||
522 | /// `struct S { ... }` - type namespace only. | ||
523 | Record, | ||
524 | /// `struct S(...);` | ||
525 | Tuple, | ||
526 | /// `struct S;` | ||
527 | Unit, | ||
528 | } | ||
529 | |||
530 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
531 | pub struct Union { | ||
532 | pub name: Name, | ||
533 | pub visibility: RawVisibilityId, | ||
534 | pub generic_params: GenericParamsId, | ||
535 | pub fields: Fields, | ||
536 | pub ast_id: FileAstId<ast::UnionDef>, | ||
537 | } | ||
538 | |||
539 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
540 | pub struct Enum { | ||
541 | pub name: Name, | ||
542 | pub visibility: RawVisibilityId, | ||
543 | pub generic_params: GenericParamsId, | ||
544 | pub variants: IdRange<Variant>, | ||
545 | pub ast_id: FileAstId<ast::EnumDef>, | ||
546 | } | ||
547 | |||
548 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
549 | pub struct Const { | ||
550 | /// const _: () = (); | ||
551 | pub name: Option<Name>, | ||
552 | pub visibility: RawVisibilityId, | ||
553 | pub type_ref: TypeRef, | ||
554 | pub ast_id: FileAstId<ast::ConstDef>, | ||
555 | } | ||
556 | |||
557 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
558 | pub struct Static { | ||
559 | pub name: Name, | ||
560 | pub visibility: RawVisibilityId, | ||
561 | pub mutable: bool, | ||
562 | pub type_ref: TypeRef, | ||
563 | pub ast_id: FileAstId<ast::StaticDef>, | ||
564 | } | ||
565 | |||
566 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
567 | pub struct Trait { | ||
568 | pub name: Name, | ||
569 | pub visibility: RawVisibilityId, | ||
570 | pub generic_params: GenericParamsId, | ||
571 | pub auto: bool, | ||
572 | pub items: Box<[AssocItem]>, | ||
573 | pub ast_id: FileAstId<ast::TraitDef>, | ||
574 | } | ||
575 | |||
576 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
577 | pub struct Impl { | ||
578 | pub generic_params: GenericParamsId, | ||
579 | pub target_trait: Option<TypeRef>, | ||
580 | pub target_type: TypeRef, | ||
581 | pub is_negative: bool, | ||
582 | pub items: Box<[AssocItem]>, | ||
583 | pub ast_id: FileAstId<ast::ImplDef>, | ||
584 | } | ||
585 | |||
586 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
587 | pub struct TypeAlias { | ||
588 | pub name: Name, | ||
589 | pub visibility: RawVisibilityId, | ||
590 | /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`. | ||
591 | pub bounds: Box<[TypeBound]>, | ||
592 | pub generic_params: GenericParamsId, | ||
593 | pub type_ref: Option<TypeRef>, | ||
594 | pub ast_id: FileAstId<ast::TypeAliasDef>, | ||
595 | } | ||
596 | |||
597 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
598 | pub struct Mod { | ||
599 | pub name: Name, | ||
600 | pub visibility: RawVisibilityId, | ||
601 | pub kind: ModKind, | ||
602 | pub ast_id: FileAstId<ast::Module>, | ||
603 | } | ||
604 | |||
605 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
606 | pub enum ModKind { | ||
607 | /// `mod m { ... }` | ||
608 | Inline { items: Box<[ModItem]> }, | ||
609 | |||
610 | /// `mod m;` | ||
611 | Outline {}, | ||
612 | } | ||
613 | |||
614 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
615 | pub struct MacroCall { | ||
616 | /// For `macro_rules!` declarations, this is the name of the declared macro. | ||
617 | pub name: Option<Name>, | ||
618 | /// Path to the called macro. | ||
619 | pub path: ModPath, | ||
620 | /// Has `#[macro_export]`. | ||
621 | pub is_export: bool, | ||
622 | /// Has `#[macro_export(local_inner_macros)]`. | ||
623 | pub is_local_inner: bool, | ||
624 | /// Has `#[rustc_builtin_macro]`. | ||
625 | pub is_builtin: bool, | ||
626 | pub ast_id: FileAstId<ast::MacroCall>, | ||
627 | } | ||
628 | |||
629 | // NB: There's no `FileAstId` for `Expr`. The only case where this would be useful is for array | ||
630 | // lengths, but we don't do much with them yet. | ||
631 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
632 | pub struct Expr; | ||
633 | |||
634 | macro_rules! impl_froms { | ||
635 | ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => { | ||
636 | $( | ||
637 | impl From<$t> for $e { | ||
638 | fn from(it: $t) -> $e { | ||
639 | $e::$v(it) | ||
640 | } | ||
641 | } | ||
642 | )* | ||
643 | } | ||
644 | } | ||
645 | |||
646 | impl ModItem { | ||
647 | pub fn as_assoc_item(&self) -> Option<AssocItem> { | ||
648 | match self { | ||
649 | ModItem::Import(_) | ||
650 | | ModItem::ExternCrate(_) | ||
651 | | ModItem::Struct(_) | ||
652 | | ModItem::Union(_) | ||
653 | | ModItem::Enum(_) | ||
654 | | ModItem::Static(_) | ||
655 | | ModItem::Trait(_) | ||
656 | | ModItem::Impl(_) | ||
657 | | ModItem::Mod(_) => None, | ||
658 | ModItem::MacroCall(call) => Some(AssocItem::MacroCall(*call)), | ||
659 | ModItem::Const(konst) => Some(AssocItem::Const(*konst)), | ||
660 | ModItem::TypeAlias(alias) => Some(AssocItem::TypeAlias(*alias)), | ||
661 | ModItem::Function(func) => Some(AssocItem::Function(*func)), | ||
662 | } | ||
663 | } | ||
664 | |||
665 | pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> { | ||
666 | N::id_from_mod_item(self) | ||
667 | } | ||
668 | } | ||
669 | |||
670 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
671 | pub enum AssocItem { | ||
672 | Function(FileItemTreeId<Function>), | ||
673 | TypeAlias(FileItemTreeId<TypeAlias>), | ||
674 | Const(FileItemTreeId<Const>), | ||
675 | MacroCall(FileItemTreeId<MacroCall>), | ||
676 | } | ||
677 | |||
678 | impl_froms!(AssocItem { | ||
679 | Function(FileItemTreeId<Function>), | ||
680 | TypeAlias(FileItemTreeId<TypeAlias>), | ||
681 | Const(FileItemTreeId<Const>), | ||
682 | MacroCall(FileItemTreeId<MacroCall>), | ||
683 | }); | ||
684 | |||
685 | impl From<AssocItem> for ModItem { | ||
686 | fn from(item: AssocItem) -> Self { | ||
687 | match item { | ||
688 | AssocItem::Function(it) => it.into(), | ||
689 | AssocItem::TypeAlias(it) => it.into(), | ||
690 | AssocItem::Const(it) => it.into(), | ||
691 | AssocItem::MacroCall(it) => it.into(), | ||
692 | } | ||
693 | } | ||
694 | } | ||
695 | |||
696 | #[derive(Debug, Eq, PartialEq)] | ||
697 | pub struct Variant { | ||
698 | pub name: Name, | ||
699 | pub fields: Fields, | ||
700 | } | ||
701 | |||
702 | pub struct IdRange<T> { | ||
703 | range: Range<u32>, | ||
704 | _p: PhantomData<T>, | ||
705 | } | ||
706 | |||
707 | impl<T> IdRange<T> { | ||
708 | fn new(range: Range<Idx<T>>) -> Self { | ||
709 | Self { range: range.start.into_raw().into()..range.end.into_raw().into(), _p: PhantomData } | ||
710 | } | ||
711 | } | ||
712 | |||
713 | impl<T> Iterator for IdRange<T> { | ||
714 | type Item = Idx<T>; | ||
715 | fn next(&mut self) -> Option<Self::Item> { | ||
716 | self.range.next().map(|raw| Idx::from_raw(raw.into())) | ||
717 | } | ||
718 | } | ||
719 | |||
720 | impl<T> fmt::Debug for IdRange<T> { | ||
721 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
722 | f.debug_tuple(&format!("IdRange::<{}>", type_name::<T>())).field(&self.range).finish() | ||
723 | } | ||
724 | } | ||
725 | |||
726 | impl<T> Clone for IdRange<T> { | ||
727 | fn clone(&self) -> Self { | ||
728 | Self { range: self.range.clone(), _p: PhantomData } | ||
729 | } | ||
730 | } | ||
731 | |||
732 | impl<T> PartialEq for IdRange<T> { | ||
733 | fn eq(&self, other: &Self) -> bool { | ||
734 | self.range == other.range | ||
735 | } | ||
736 | } | ||
737 | |||
738 | impl<T> Eq for IdRange<T> {} | ||
739 | |||
740 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
741 | pub enum Fields { | ||
742 | Record(IdRange<Field>), | ||
743 | Tuple(IdRange<Field>), | ||
744 | Unit, | ||
745 | } | ||
746 | |||
747 | /// A single field of an enum variant or struct | ||
748 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
749 | pub struct Field { | ||
750 | pub name: Name, | ||
751 | pub type_ref: TypeRef, | ||
752 | pub visibility: RawVisibilityId, | ||
753 | } | ||
diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs new file mode 100644 index 000000000..5149dd141 --- /dev/null +++ b/crates/ra_hir_def/src/item_tree/lower.rs | |||
@@ -0,0 +1,698 @@ | |||
1 | //! AST -> `ItemTree` lowering code. | ||
2 | |||
3 | use super::*; | ||
4 | use crate::{ | ||
5 | attr::Attrs, | ||
6 | generics::{GenericParams, TypeParamData, TypeParamProvenance}, | ||
7 | }; | ||
8 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId}; | ||
9 | use ra_arena::map::ArenaMap; | ||
10 | use ra_syntax::{ | ||
11 | ast::{self, ModuleItemOwner}, | ||
12 | SyntaxNode, | ||
13 | }; | ||
14 | use smallvec::SmallVec; | ||
15 | use std::{collections::hash_map::Entry, mem, sync::Arc}; | ||
16 | |||
17 | fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> { | ||
18 | FileItemTreeId { index, _p: PhantomData } | ||
19 | } | ||
20 | |||
21 | struct ModItems(SmallVec<[ModItem; 1]>); | ||
22 | |||
23 | impl<T> From<T> for ModItems | ||
24 | where | ||
25 | T: Into<ModItem>, | ||
26 | { | ||
27 | fn from(t: T) -> Self { | ||
28 | ModItems(SmallVec::from_buf([t.into(); 1])) | ||
29 | } | ||
30 | } | ||
31 | |||
32 | pub(super) struct Ctx { | ||
33 | tree: ItemTree, | ||
34 | hygiene: Hygiene, | ||
35 | file: HirFileId, | ||
36 | source_ast_id_map: Arc<AstIdMap>, | ||
37 | body_ctx: crate::body::LowerCtx, | ||
38 | inner_items: Vec<ModItem>, | ||
39 | forced_visibility: Option<RawVisibilityId>, | ||
40 | } | ||
41 | |||
42 | impl Ctx { | ||
43 | pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self { | ||
44 | Self { | ||
45 | tree: ItemTree::empty(), | ||
46 | hygiene, | ||
47 | file, | ||
48 | source_ast_id_map: db.ast_id_map(file), | ||
49 | body_ctx: crate::body::LowerCtx::new(db, file), | ||
50 | inner_items: Vec::new(), | ||
51 | forced_visibility: None, | ||
52 | } | ||
53 | } | ||
54 | |||
55 | pub(super) fn lower_module_items(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree { | ||
56 | self.tree.top_level = item_owner | ||
57 | .items() | ||
58 | .flat_map(|item| self.lower_mod_item(&item, false)) | ||
59 | .flat_map(|items| items.0) | ||
60 | .collect(); | ||
61 | self.tree | ||
62 | } | ||
63 | |||
64 | pub(super) fn lower_inner_items(mut self, within: &SyntaxNode) -> ItemTree { | ||
65 | self.collect_inner_items(within); | ||
66 | self.tree | ||
67 | } | ||
68 | |||
69 | fn data(&mut self) -> &mut ItemTreeData { | ||
70 | self.tree.data_mut() | ||
71 | } | ||
72 | |||
73 | fn lower_mod_item(&mut self, item: &ast::ModuleItem, inner: bool) -> Option<ModItems> { | ||
74 | assert!(inner || self.inner_items.is_empty()); | ||
75 | |||
76 | // Collect inner items for 1-to-1-lowered items. | ||
77 | match item { | ||
78 | ast::ModuleItem::StructDef(_) | ||
79 | | ast::ModuleItem::UnionDef(_) | ||
80 | | ast::ModuleItem::EnumDef(_) | ||
81 | | ast::ModuleItem::FnDef(_) | ||
82 | | ast::ModuleItem::TypeAliasDef(_) | ||
83 | | ast::ModuleItem::ConstDef(_) | ||
84 | | ast::ModuleItem::StaticDef(_) | ||
85 | | ast::ModuleItem::MacroCall(_) => { | ||
86 | // Skip this if we're already collecting inner items. We'll descend into all nodes | ||
87 | // already. | ||
88 | if !inner { | ||
89 | self.collect_inner_items(item.syntax()); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | // These are handled in their respective `lower_X` method (since we can't just blindly | ||
94 | // walk them). | ||
95 | ast::ModuleItem::TraitDef(_) | ||
96 | | ast::ModuleItem::ImplDef(_) | ||
97 | | ast::ModuleItem::ExternBlock(_) => {} | ||
98 | |||
99 | // These don't have inner items. | ||
100 | ast::ModuleItem::Module(_) | ||
101 | | ast::ModuleItem::ExternCrateItem(_) | ||
102 | | ast::ModuleItem::UseItem(_) => {} | ||
103 | }; | ||
104 | |||
105 | let attrs = Attrs::new(item, &self.hygiene); | ||
106 | let items = match item { | ||
107 | ast::ModuleItem::StructDef(ast) => self.lower_struct(ast).map(Into::into), | ||
108 | ast::ModuleItem::UnionDef(ast) => self.lower_union(ast).map(Into::into), | ||
109 | ast::ModuleItem::EnumDef(ast) => self.lower_enum(ast).map(Into::into), | ||
110 | ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into), | ||
111 | ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into), | ||
112 | ast::ModuleItem::StaticDef(ast) => self.lower_static(ast).map(Into::into), | ||
113 | ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()), | ||
114 | ast::ModuleItem::Module(ast) => self.lower_module(ast).map(Into::into), | ||
115 | ast::ModuleItem::TraitDef(ast) => self.lower_trait(ast).map(Into::into), | ||
116 | ast::ModuleItem::ImplDef(ast) => self.lower_impl(ast).map(Into::into), | ||
117 | ast::ModuleItem::UseItem(ast) => Some(ModItems( | ||
118 | self.lower_use(ast).into_iter().map(Into::into).collect::<SmallVec<_>>(), | ||
119 | )), | ||
120 | ast::ModuleItem::ExternCrateItem(ast) => self.lower_extern_crate(ast).map(Into::into), | ||
121 | ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), | ||
122 | ast::ModuleItem::ExternBlock(ast) => { | ||
123 | Some(ModItems(self.lower_extern_block(ast).into_iter().collect::<SmallVec<_>>())) | ||
124 | } | ||
125 | }; | ||
126 | |||
127 | if !attrs.is_empty() { | ||
128 | for item in items.iter().flat_map(|items| &items.0) { | ||
129 | self.add_attrs((*item).into(), attrs.clone()); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | items | ||
134 | } | ||
135 | |||
136 | fn add_attrs(&mut self, item: AttrOwner, attrs: Attrs) { | ||
137 | match self.tree.attrs.entry(item) { | ||
138 | Entry::Occupied(mut entry) => { | ||
139 | *entry.get_mut() = entry.get().merge(attrs); | ||
140 | } | ||
141 | Entry::Vacant(entry) => { | ||
142 | entry.insert(attrs); | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | |||
147 | fn collect_inner_items(&mut self, container: &SyntaxNode) { | ||
148 | let forced_vis = self.forced_visibility.take(); | ||
149 | let mut inner_items = mem::take(&mut self.tree.inner_items); | ||
150 | inner_items.extend( | ||
151 | container.descendants().skip(1).filter_map(ast::ModuleItem::cast).filter_map(|item| { | ||
152 | let ast_id = self.source_ast_id_map.ast_id(&item); | ||
153 | Some((ast_id, self.lower_mod_item(&item, true)?.0)) | ||
154 | }), | ||
155 | ); | ||
156 | self.tree.inner_items = inner_items; | ||
157 | self.forced_visibility = forced_vis; | ||
158 | } | ||
159 | |||
160 | fn lower_assoc_item(&mut self, item: &ast::ModuleItem) -> Option<AssocItem> { | ||
161 | match item { | ||
162 | ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into), | ||
163 | ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into), | ||
164 | ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()), | ||
165 | ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), | ||
166 | _ => None, | ||
167 | } | ||
168 | } | ||
169 | |||
170 | fn lower_struct(&mut self, strukt: &ast::StructDef) -> Option<FileItemTreeId<Struct>> { | ||
171 | let visibility = self.lower_visibility(strukt); | ||
172 | let name = strukt.name()?.as_name(); | ||
173 | let generic_params = self.lower_generic_params(GenericsOwner::Struct, strukt); | ||
174 | let fields = self.lower_fields(&strukt.kind()); | ||
175 | let ast_id = self.source_ast_id_map.ast_id(strukt); | ||
176 | let kind = match strukt.kind() { | ||
177 | ast::StructKind::Record(_) => StructDefKind::Record, | ||
178 | ast::StructKind::Tuple(_) => StructDefKind::Tuple, | ||
179 | ast::StructKind::Unit => StructDefKind::Unit, | ||
180 | }; | ||
181 | let res = Struct { name, visibility, generic_params, fields, ast_id, kind }; | ||
182 | Some(id(self.data().structs.alloc(res))) | ||
183 | } | ||
184 | |||
185 | fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields { | ||
186 | match strukt_kind { | ||
187 | ast::StructKind::Record(it) => { | ||
188 | let range = self.lower_record_fields(it); | ||
189 | Fields::Record(range) | ||
190 | } | ||
191 | ast::StructKind::Tuple(it) => { | ||
192 | let range = self.lower_tuple_fields(it); | ||
193 | Fields::Tuple(range) | ||
194 | } | ||
195 | ast::StructKind::Unit => Fields::Unit, | ||
196 | } | ||
197 | } | ||
198 | |||
199 | fn lower_record_fields(&mut self, fields: &ast::RecordFieldDefList) -> IdRange<Field> { | ||
200 | let start = self.next_field_idx(); | ||
201 | for field in fields.fields() { | ||
202 | if let Some(data) = self.lower_record_field(&field) { | ||
203 | let idx = self.data().fields.alloc(data); | ||
204 | self.add_attrs(idx.into(), Attrs::new(&field, &self.hygiene)); | ||
205 | } | ||
206 | } | ||
207 | let end = self.next_field_idx(); | ||
208 | IdRange::new(start..end) | ||
209 | } | ||
210 | |||
211 | fn lower_record_field(&mut self, field: &ast::RecordFieldDef) -> Option<Field> { | ||
212 | let name = field.name()?.as_name(); | ||
213 | let visibility = self.lower_visibility(field); | ||
214 | let type_ref = self.lower_type_ref(&field.ascribed_type()?); | ||
215 | let res = Field { name, type_ref, visibility }; | ||
216 | Some(res) | ||
217 | } | ||
218 | |||
219 | fn lower_tuple_fields(&mut self, fields: &ast::TupleFieldDefList) -> IdRange<Field> { | ||
220 | let start = self.next_field_idx(); | ||
221 | for (i, field) in fields.fields().enumerate() { | ||
222 | if let Some(data) = self.lower_tuple_field(i, &field) { | ||
223 | let idx = self.data().fields.alloc(data); | ||
224 | self.add_attrs(idx.into(), Attrs::new(&field, &self.hygiene)); | ||
225 | } | ||
226 | } | ||
227 | let end = self.next_field_idx(); | ||
228 | IdRange::new(start..end) | ||
229 | } | ||
230 | |||
231 | fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleFieldDef) -> Option<Field> { | ||
232 | let name = Name::new_tuple_field(idx); | ||
233 | let visibility = self.lower_visibility(field); | ||
234 | let type_ref = self.lower_type_ref(&field.type_ref()?); | ||
235 | let res = Field { name, type_ref, visibility }; | ||
236 | Some(res) | ||
237 | } | ||
238 | |||
239 | fn lower_union(&mut self, union: &ast::UnionDef) -> Option<FileItemTreeId<Union>> { | ||
240 | let visibility = self.lower_visibility(union); | ||
241 | let name = union.name()?.as_name(); | ||
242 | let generic_params = self.lower_generic_params(GenericsOwner::Union, union); | ||
243 | let fields = match union.record_field_def_list() { | ||
244 | Some(record_field_def_list) => { | ||
245 | self.lower_fields(&StructKind::Record(record_field_def_list)) | ||
246 | } | ||
247 | None => Fields::Record(IdRange::new(self.next_field_idx()..self.next_field_idx())), | ||
248 | }; | ||
249 | let ast_id = self.source_ast_id_map.ast_id(union); | ||
250 | let res = Union { name, visibility, generic_params, fields, ast_id }; | ||
251 | Some(id(self.data().unions.alloc(res))) | ||
252 | } | ||
253 | |||
254 | fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<FileItemTreeId<Enum>> { | ||
255 | let visibility = self.lower_visibility(enum_); | ||
256 | let name = enum_.name()?.as_name(); | ||
257 | let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_); | ||
258 | let variants = match &enum_.variant_list() { | ||
259 | Some(variant_list) => self.lower_variants(variant_list), | ||
260 | None => IdRange::new(self.next_variant_idx()..self.next_variant_idx()), | ||
261 | }; | ||
262 | let ast_id = self.source_ast_id_map.ast_id(enum_); | ||
263 | let res = Enum { name, visibility, generic_params, variants, ast_id }; | ||
264 | Some(id(self.data().enums.alloc(res))) | ||
265 | } | ||
266 | |||
267 | fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> IdRange<Variant> { | ||
268 | let start = self.next_variant_idx(); | ||
269 | for variant in variants.variants() { | ||
270 | if let Some(data) = self.lower_variant(&variant) { | ||
271 | let idx = self.data().variants.alloc(data); | ||
272 | self.add_attrs(idx.into(), Attrs::new(&variant, &self.hygiene)); | ||
273 | } | ||
274 | } | ||
275 | let end = self.next_variant_idx(); | ||
276 | IdRange::new(start..end) | ||
277 | } | ||
278 | |||
279 | fn lower_variant(&mut self, variant: &ast::EnumVariant) -> Option<Variant> { | ||
280 | let name = variant.name()?.as_name(); | ||
281 | let fields = self.lower_fields(&variant.kind()); | ||
282 | let res = Variant { name, fields }; | ||
283 | Some(res) | ||
284 | } | ||
285 | |||
286 | fn lower_function(&mut self, func: &ast::FnDef) -> Option<FileItemTreeId<Function>> { | ||
287 | let visibility = self.lower_visibility(func); | ||
288 | let name = func.name()?.as_name(); | ||
289 | |||
290 | let mut params = Vec::new(); | ||
291 | let mut has_self_param = false; | ||
292 | if let Some(param_list) = func.param_list() { | ||
293 | if let Some(self_param) = param_list.self_param() { | ||
294 | let self_type = match self_param.ascribed_type() { | ||
295 | Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), | ||
296 | None => { | ||
297 | let self_type = TypeRef::Path(name![Self].into()); | ||
298 | match self_param.kind() { | ||
299 | ast::SelfParamKind::Owned => self_type, | ||
300 | ast::SelfParamKind::Ref => { | ||
301 | TypeRef::Reference(Box::new(self_type), Mutability::Shared) | ||
302 | } | ||
303 | ast::SelfParamKind::MutRef => { | ||
304 | TypeRef::Reference(Box::new(self_type), Mutability::Mut) | ||
305 | } | ||
306 | } | ||
307 | } | ||
308 | }; | ||
309 | params.push(self_type); | ||
310 | has_self_param = true; | ||
311 | } | ||
312 | for param in param_list.params() { | ||
313 | let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ascribed_type()); | ||
314 | params.push(type_ref); | ||
315 | } | ||
316 | } | ||
317 | let ret_type = match func.ret_type().and_then(|rt| rt.type_ref()) { | ||
318 | Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), | ||
319 | _ => TypeRef::unit(), | ||
320 | }; | ||
321 | |||
322 | let ret_type = if func.async_token().is_some() { | ||
323 | let future_impl = desugar_future_path(ret_type); | ||
324 | let ty_bound = TypeBound::Path(future_impl); | ||
325 | TypeRef::ImplTrait(vec![ty_bound]) | ||
326 | } else { | ||
327 | ret_type | ||
328 | }; | ||
329 | |||
330 | let ast_id = self.source_ast_id_map.ast_id(func); | ||
331 | let mut res = Function { | ||
332 | name, | ||
333 | visibility, | ||
334 | generic_params: GenericParamsId::EMPTY, | ||
335 | has_self_param, | ||
336 | is_unsafe: func.unsafe_token().is_some(), | ||
337 | params: params.into_boxed_slice(), | ||
338 | ret_type, | ||
339 | ast_id, | ||
340 | }; | ||
341 | res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func); | ||
342 | |||
343 | Some(id(self.data().functions.alloc(res))) | ||
344 | } | ||
345 | |||
346 | fn lower_type_alias( | ||
347 | &mut self, | ||
348 | type_alias: &ast::TypeAliasDef, | ||
349 | ) -> Option<FileItemTreeId<TypeAlias>> { | ||
350 | let name = type_alias.name()?.as_name(); | ||
351 | let type_ref = type_alias.type_ref().map(|it| self.lower_type_ref(&it)); | ||
352 | let visibility = self.lower_visibility(type_alias); | ||
353 | let bounds = self.lower_type_bounds(type_alias); | ||
354 | let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias); | ||
355 | let ast_id = self.source_ast_id_map.ast_id(type_alias); | ||
356 | let res = TypeAlias { | ||
357 | name, | ||
358 | visibility, | ||
359 | bounds: bounds.into_boxed_slice(), | ||
360 | generic_params, | ||
361 | type_ref, | ||
362 | ast_id, | ||
363 | }; | ||
364 | Some(id(self.data().type_aliases.alloc(res))) | ||
365 | } | ||
366 | |||
367 | fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<FileItemTreeId<Static>> { | ||
368 | let name = static_.name()?.as_name(); | ||
369 | let type_ref = self.lower_type_ref_opt(static_.ascribed_type()); | ||
370 | let visibility = self.lower_visibility(static_); | ||
371 | let mutable = static_.mut_token().is_some(); | ||
372 | let ast_id = self.source_ast_id_map.ast_id(static_); | ||
373 | let res = Static { name, visibility, mutable, type_ref, ast_id }; | ||
374 | Some(id(self.data().statics.alloc(res))) | ||
375 | } | ||
376 | |||
377 | fn lower_const(&mut self, konst: &ast::ConstDef) -> FileItemTreeId<Const> { | ||
378 | let name = konst.name().map(|it| it.as_name()); | ||
379 | let type_ref = self.lower_type_ref_opt(konst.ascribed_type()); | ||
380 | let visibility = self.lower_visibility(konst); | ||
381 | let ast_id = self.source_ast_id_map.ast_id(konst); | ||
382 | let res = Const { name, visibility, type_ref, ast_id }; | ||
383 | id(self.data().consts.alloc(res)) | ||
384 | } | ||
385 | |||
386 | fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> { | ||
387 | let name = module.name()?.as_name(); | ||
388 | let visibility = self.lower_visibility(module); | ||
389 | let kind = if module.semicolon_token().is_some() { | ||
390 | ModKind::Outline {} | ||
391 | } else { | ||
392 | ModKind::Inline { | ||
393 | items: module | ||
394 | .item_list() | ||
395 | .map(|list| { | ||
396 | list.items() | ||
397 | .flat_map(|item| self.lower_mod_item(&item, false)) | ||
398 | .flat_map(|items| items.0) | ||
399 | .collect() | ||
400 | }) | ||
401 | .unwrap_or_else(|| { | ||
402 | mark::hit!(name_res_works_for_broken_modules); | ||
403 | Box::new([]) as Box<[_]> | ||
404 | }), | ||
405 | } | ||
406 | }; | ||
407 | let ast_id = self.source_ast_id_map.ast_id(module); | ||
408 | let res = Mod { name, visibility, kind, ast_id }; | ||
409 | Some(id(self.data().mods.alloc(res))) | ||
410 | } | ||
411 | |||
412 | fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<FileItemTreeId<Trait>> { | ||
413 | let name = trait_def.name()?.as_name(); | ||
414 | let visibility = self.lower_visibility(trait_def); | ||
415 | let generic_params = | ||
416 | self.lower_generic_params_and_inner_items(GenericsOwner::Trait(trait_def), trait_def); | ||
417 | let auto = trait_def.auto_token().is_some(); | ||
418 | let items = trait_def.item_list().map(|list| { | ||
419 | self.with_inherited_visibility(visibility, |this| { | ||
420 | list.items() | ||
421 | .filter_map(|item| { | ||
422 | let attrs = Attrs::new(&item, &this.hygiene); | ||
423 | this.collect_inner_items(item.syntax()); | ||
424 | this.lower_assoc_item(&item).map(|item| { | ||
425 | this.add_attrs(ModItem::from(item).into(), attrs); | ||
426 | item | ||
427 | }) | ||
428 | }) | ||
429 | .collect() | ||
430 | }) | ||
431 | }); | ||
432 | let ast_id = self.source_ast_id_map.ast_id(trait_def); | ||
433 | let res = Trait { | ||
434 | name, | ||
435 | visibility, | ||
436 | generic_params, | ||
437 | auto, | ||
438 | items: items.unwrap_or_default(), | ||
439 | ast_id, | ||
440 | }; | ||
441 | Some(id(self.data().traits.alloc(res))) | ||
442 | } | ||
443 | |||
444 | fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<FileItemTreeId<Impl>> { | ||
445 | let generic_params = | ||
446 | self.lower_generic_params_and_inner_items(GenericsOwner::Impl, impl_def); | ||
447 | let target_trait = impl_def.target_trait().map(|tr| self.lower_type_ref(&tr)); | ||
448 | let target_type = self.lower_type_ref(&impl_def.target_type()?); | ||
449 | let is_negative = impl_def.excl_token().is_some(); | ||
450 | |||
451 | // We cannot use `assoc_items()` here as that does not include macro calls. | ||
452 | let items = impl_def | ||
453 | .item_list()? | ||
454 | .items() | ||
455 | .filter_map(|item| { | ||
456 | self.collect_inner_items(item.syntax()); | ||
457 | let assoc = self.lower_assoc_item(&item)?; | ||
458 | let attrs = Attrs::new(&item, &self.hygiene); | ||
459 | self.add_attrs(ModItem::from(assoc).into(), attrs); | ||
460 | Some(assoc) | ||
461 | }) | ||
462 | .collect(); | ||
463 | let ast_id = self.source_ast_id_map.ast_id(impl_def); | ||
464 | let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id }; | ||
465 | Some(id(self.data().impls.alloc(res))) | ||
466 | } | ||
467 | |||
468 | fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<FileItemTreeId<Import>> { | ||
469 | // FIXME: cfg_attr | ||
470 | let is_prelude = use_item.has_atom_attr("prelude_import"); | ||
471 | let visibility = self.lower_visibility(use_item); | ||
472 | let ast_id = self.source_ast_id_map.ast_id(use_item); | ||
473 | |||
474 | // Every use item can expand to many `Import`s. | ||
475 | let mut imports = Vec::new(); | ||
476 | let tree = self.tree.data_mut(); | ||
477 | ModPath::expand_use_item( | ||
478 | InFile::new(self.file, use_item.clone()), | ||
479 | &self.hygiene, | ||
480 | |path, _tree, is_glob, alias| { | ||
481 | imports.push(id(tree.imports.alloc(Import { | ||
482 | path, | ||
483 | alias, | ||
484 | visibility, | ||
485 | is_glob, | ||
486 | is_prelude, | ||
487 | ast_id, | ||
488 | }))); | ||
489 | }, | ||
490 | ); | ||
491 | |||
492 | imports | ||
493 | } | ||
494 | |||
495 | fn lower_extern_crate( | ||
496 | &mut self, | ||
497 | extern_crate: &ast::ExternCrateItem, | ||
498 | ) -> Option<FileItemTreeId<ExternCrate>> { | ||
499 | let path = ModPath::from_name_ref(&extern_crate.name_ref()?); | ||
500 | let alias = extern_crate.alias().map(|a| { | ||
501 | a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias) | ||
502 | }); | ||
503 | let visibility = self.lower_visibility(extern_crate); | ||
504 | let ast_id = self.source_ast_id_map.ast_id(extern_crate); | ||
505 | // FIXME: cfg_attr | ||
506 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); | ||
507 | |||
508 | let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id }; | ||
509 | Some(id(self.data().extern_crates.alloc(res))) | ||
510 | } | ||
511 | |||
512 | fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> { | ||
513 | let name = m.name().map(|it| it.as_name()); | ||
514 | let attrs = Attrs::new(m, &self.hygiene); | ||
515 | let path = ModPath::from_src(m.path()?, &self.hygiene)?; | ||
516 | |||
517 | let ast_id = self.source_ast_id_map.ast_id(m); | ||
518 | |||
519 | // FIXME: cfg_attr | ||
520 | let export_attr = attrs.by_key("macro_export"); | ||
521 | |||
522 | let is_export = export_attr.exists(); | ||
523 | let is_local_inner = if is_export { | ||
524 | export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it { | ||
525 | tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { | ||
526 | ident.text.contains("local_inner_macros") | ||
527 | } | ||
528 | _ => false, | ||
529 | }) | ||
530 | } else { | ||
531 | false | ||
532 | }; | ||
533 | |||
534 | let is_builtin = attrs.by_key("rustc_builtin_macro").exists(); | ||
535 | let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id }; | ||
536 | Some(id(self.data().macro_calls.alloc(res))) | ||
537 | } | ||
538 | |||
539 | fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> { | ||
540 | block.extern_item_list().map_or(Vec::new(), |list| { | ||
541 | list.extern_items() | ||
542 | .filter_map(|item| { | ||
543 | self.collect_inner_items(item.syntax()); | ||
544 | let attrs = Attrs::new(&item, &self.hygiene); | ||
545 | let id: ModItem = match item { | ||
546 | ast::ExternItem::FnDef(ast) => { | ||
547 | let func = self.lower_function(&ast)?; | ||
548 | func.into() | ||
549 | } | ||
550 | ast::ExternItem::StaticDef(ast) => { | ||
551 | let statik = self.lower_static(&ast)?; | ||
552 | statik.into() | ||
553 | } | ||
554 | }; | ||
555 | self.add_attrs(id.into(), attrs); | ||
556 | Some(id) | ||
557 | }) | ||
558 | .collect() | ||
559 | }) | ||
560 | } | ||
561 | |||
562 | /// Lowers generics defined on `node` and collects inner items defined within. | ||
563 | fn lower_generic_params_and_inner_items( | ||
564 | &mut self, | ||
565 | owner: GenericsOwner<'_>, | ||
566 | node: &impl ast::TypeParamsOwner, | ||
567 | ) -> GenericParamsId { | ||
568 | // Generics are part of item headers and may contain inner items we need to collect. | ||
569 | if let Some(params) = node.type_param_list() { | ||
570 | self.collect_inner_items(params.syntax()); | ||
571 | } | ||
572 | if let Some(clause) = node.where_clause() { | ||
573 | self.collect_inner_items(clause.syntax()); | ||
574 | } | ||
575 | |||
576 | self.lower_generic_params(owner, node) | ||
577 | } | ||
578 | |||
579 | fn lower_generic_params( | ||
580 | &mut self, | ||
581 | owner: GenericsOwner<'_>, | ||
582 | node: &impl ast::TypeParamsOwner, | ||
583 | ) -> GenericParamsId { | ||
584 | let mut sm = &mut ArenaMap::default(); | ||
585 | let mut generics = GenericParams::default(); | ||
586 | match owner { | ||
587 | GenericsOwner::Function(func) => { | ||
588 | generics.fill(&self.body_ctx, sm, node); | ||
589 | // lower `impl Trait` in arguments | ||
590 | for param in &*func.params { | ||
591 | generics.fill_implicit_impl_trait_args(param); | ||
592 | } | ||
593 | } | ||
594 | GenericsOwner::Struct | ||
595 | | GenericsOwner::Enum | ||
596 | | GenericsOwner::Union | ||
597 | | GenericsOwner::TypeAlias => { | ||
598 | generics.fill(&self.body_ctx, sm, node); | ||
599 | } | ||
600 | GenericsOwner::Trait(trait_def) => { | ||
601 | // traits get the Self type as an implicit first type parameter | ||
602 | let self_param_id = generics.types.alloc(TypeParamData { | ||
603 | name: Some(name![Self]), | ||
604 | default: None, | ||
605 | provenance: TypeParamProvenance::TraitSelf, | ||
606 | }); | ||
607 | sm.insert(self_param_id, Either::Left(trait_def.clone())); | ||
608 | // add super traits as bounds on Self | ||
609 | // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar | ||
610 | let self_param = TypeRef::Path(name![Self].into()); | ||
611 | generics.fill_bounds(&self.body_ctx, trait_def, self_param); | ||
612 | |||
613 | generics.fill(&self.body_ctx, &mut sm, node); | ||
614 | } | ||
615 | GenericsOwner::Impl => { | ||
616 | // Note that we don't add `Self` here: in `impl`s, `Self` is not a | ||
617 | // type-parameter, but rather is a type-alias for impl's target | ||
618 | // type, so this is handled by the resolver. | ||
619 | generics.fill(&self.body_ctx, &mut sm, node); | ||
620 | } | ||
621 | } | ||
622 | |||
623 | self.data().generics.alloc(generics) | ||
624 | } | ||
625 | |||
626 | fn lower_type_bounds(&mut self, node: &impl ast::TypeBoundsOwner) -> Vec<TypeBound> { | ||
627 | match node.type_bound_list() { | ||
628 | Some(bound_list) => { | ||
629 | bound_list.bounds().map(|it| TypeBound::from_ast(&self.body_ctx, it)).collect() | ||
630 | } | ||
631 | None => Vec::new(), | ||
632 | } | ||
633 | } | ||
634 | |||
635 | fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId { | ||
636 | let vis = match self.forced_visibility { | ||
637 | Some(vis) => return vis, | ||
638 | None => RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene), | ||
639 | }; | ||
640 | |||
641 | self.data().vis.alloc(vis) | ||
642 | } | ||
643 | |||
644 | fn lower_type_ref(&self, type_ref: &ast::TypeRef) -> TypeRef { | ||
645 | TypeRef::from_ast(&self.body_ctx, type_ref.clone()) | ||
646 | } | ||
647 | fn lower_type_ref_opt(&self, type_ref: Option<ast::TypeRef>) -> TypeRef { | ||
648 | type_ref.map(|ty| self.lower_type_ref(&ty)).unwrap_or(TypeRef::Error) | ||
649 | } | ||
650 | |||
651 | /// Forces the visibility `vis` to be used for all items lowered during execution of `f`. | ||
652 | fn with_inherited_visibility<R>( | ||
653 | &mut self, | ||
654 | vis: RawVisibilityId, | ||
655 | f: impl FnOnce(&mut Self) -> R, | ||
656 | ) -> R { | ||
657 | let old = mem::replace(&mut self.forced_visibility, Some(vis)); | ||
658 | let res = f(self); | ||
659 | self.forced_visibility = old; | ||
660 | res | ||
661 | } | ||
662 | |||
663 | fn next_field_idx(&self) -> Idx<Field> { | ||
664 | Idx::from_raw(RawId::from( | ||
665 | self.tree.data.as_ref().map_or(0, |data| data.fields.len() as u32), | ||
666 | )) | ||
667 | } | ||
668 | fn next_variant_idx(&self) -> Idx<Variant> { | ||
669 | Idx::from_raw(RawId::from( | ||
670 | self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32), | ||
671 | )) | ||
672 | } | ||
673 | } | ||
674 | |||
675 | fn desugar_future_path(orig: TypeRef) -> Path { | ||
676 | let path = path![core::future::Future]; | ||
677 | let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); | ||
678 | let mut last = GenericArgs::empty(); | ||
679 | let binding = | ||
680 | AssociatedTypeBinding { name: name![Output], type_ref: Some(orig), bounds: Vec::new() }; | ||
681 | last.bindings.push(binding); | ||
682 | generic_args.push(Some(Arc::new(last))); | ||