diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir/src/semantics.rs | 9 | ||||
-rw-r--r-- | crates/ide/src/completion/completion_context.rs | 2 | ||||
-rw-r--r-- | crates/ssr/src/resolving.rs | 4 | ||||
-rw-r--r-- | crates/vfs/src/vfs_path.rs | 139 |
4 files changed, 139 insertions, 15 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index d8beac98a..3953017c3 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -112,14 +112,13 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
112 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { | 112 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { |
113 | self.imp.expand(macro_call) | 113 | self.imp.expand(macro_call) |
114 | } | 114 | } |
115 | 115 | pub fn speculative_expand( | |
116 | pub fn expand_hypothetical( | ||
117 | &self, | 116 | &self, |
118 | actual_macro_call: &ast::MacroCall, | 117 | actual_macro_call: &ast::MacroCall, |
119 | hypothetical_args: &ast::TokenTree, | 118 | hypothetical_args: &ast::TokenTree, |
120 | token_to_map: SyntaxToken, | 119 | token_to_map: SyntaxToken, |
121 | ) -> Option<(SyntaxNode, SyntaxToken)> { | 120 | ) -> Option<(SyntaxNode, SyntaxToken)> { |
122 | self.imp.expand_hypothetical(actual_macro_call, hypothetical_args, token_to_map) | 121 | self.imp.speculative_expand(actual_macro_call, hypothetical_args, token_to_map) |
123 | } | 122 | } |
124 | 123 | ||
125 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { | 124 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { |
@@ -311,7 +310,7 @@ impl<'db> SemanticsImpl<'db> { | |||
311 | Some(node) | 310 | Some(node) |
312 | } | 311 | } |
313 | 312 | ||
314 | fn expand_hypothetical( | 313 | fn speculative_expand( |
315 | &self, | 314 | &self, |
316 | actual_macro_call: &ast::MacroCall, | 315 | actual_macro_call: &ast::MacroCall, |
317 | hypothetical_args: &ast::TokenTree, | 316 | hypothetical_args: &ast::TokenTree, |
@@ -756,7 +755,7 @@ impl<'a> SemanticsScope<'a> { | |||
756 | 755 | ||
757 | /// Resolve a path as-if it was written at the given scope. This is | 756 | /// Resolve a path as-if it was written at the given scope. This is |
758 | /// necessary a heuristic, as it doesn't take hygiene into account. | 757 | /// necessary a heuristic, as it doesn't take hygiene into account. |
759 | pub fn resolve_hypothetical(&self, path: &ast::Path) -> Option<PathResolution> { | 758 | pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> { |
760 | let hygiene = Hygiene::new(self.db.upcast(), self.file_id); | 759 | let hygiene = Hygiene::new(self.db.upcast(), self.file_id); |
761 | let path = Path::from_src(path.clone(), &hygiene)?; | 760 | let path = Path::from_src(path.clone(), &hygiene)?; |
762 | self.resolve_hir_path(&path) | 761 | self.resolve_hir_path(&path) |
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 3857dce67..85456a66f 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs | |||
@@ -185,7 +185,7 @@ impl<'a> CompletionContext<'a> { | |||
185 | }; | 185 | }; |
186 | if let (Some(actual_expansion), Some(hypothetical_expansion)) = ( | 186 | if let (Some(actual_expansion), Some(hypothetical_expansion)) = ( |
187 | ctx.sema.expand(&actual_macro_call), | 187 | ctx.sema.expand(&actual_macro_call), |
188 | ctx.sema.expand_hypothetical( | 188 | ctx.sema.speculative_expand( |
189 | &actual_macro_call, | 189 | &actual_macro_call, |
190 | &hypothetical_args, | 190 | &hypothetical_args, |
191 | fake_ident_token, | 191 | fake_ident_token, |
diff --git a/crates/ssr/src/resolving.rs b/crates/ssr/src/resolving.rs index 4441fb426..b932132d5 100644 --- a/crates/ssr/src/resolving.rs +++ b/crates/ssr/src/resolving.rs | |||
@@ -212,13 +212,13 @@ impl<'db> ResolutionScope<'db> { | |||
212 | // First try resolving the whole path. This will work for things like | 212 | // First try resolving the whole path. This will work for things like |
213 | // `std::collections::HashMap`, but will fail for things like | 213 | // `std::collections::HashMap`, but will fail for things like |
214 | // `std::collections::HashMap::new`. | 214 | // `std::collections::HashMap::new`. |
215 | if let Some(resolution) = self.scope.resolve_hypothetical(&path) { | 215 | if let Some(resolution) = self.scope.speculative_resolve(&path) { |
216 | return Some(resolution); | 216 | return Some(resolution); |
217 | } | 217 | } |
218 | // Resolution failed, try resolving the qualifier (e.g. `std::collections::HashMap` and if | 218 | // Resolution failed, try resolving the qualifier (e.g. `std::collections::HashMap` and if |
219 | // that succeeds, then iterate through the candidates on the resolved type with the provided | 219 | // that succeeds, then iterate through the candidates on the resolved type with the provided |
220 | // name. | 220 | // name. |
221 | let resolved_qualifier = self.scope.resolve_hypothetical(&path.qualifier()?)?; | 221 | let resolved_qualifier = self.scope.speculative_resolve(&path.qualifier()?)?; |
222 | if let hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) = resolved_qualifier { | 222 | if let hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) = resolved_qualifier { |
223 | let name = path.segment()?.name_ref()?; | 223 | let name = path.segment()?.name_ref()?; |
224 | adt.ty(self.scope.db).iterate_path_candidates( | 224 | adt.ty(self.scope.db).iterate_path_candidates( |
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs index 04a42264e..944a702df 100644 --- a/crates/vfs/src/vfs_path.rs +++ b/crates/vfs/src/vfs_path.rs | |||
@@ -57,23 +57,42 @@ impl VfsPath { | |||
57 | }; | 57 | }; |
58 | buf.push(tag); | 58 | buf.push(tag); |
59 | match &self.0 { | 59 | match &self.0 { |
60 | VfsPathRepr::PathBuf(it) => { | 60 | VfsPathRepr::PathBuf(path) => { |
61 | let path: &std::ffi::OsStr = it.as_os_str(); | ||
62 | #[cfg(windows)] | 61 | #[cfg(windows)] |
63 | { | 62 | { |
64 | use std::os::windows::ffi::OsStrExt; | 63 | use windows_paths::Encode; |
65 | for wchar in path.encode_wide() { | 64 | let components = path.components(); |
66 | buf.extend(wchar.to_le_bytes().iter().copied()); | 65 | let mut add_sep = false; |
66 | for component in components { | ||
67 | if add_sep { | ||
68 | windows_paths::SEP.encode(buf); | ||
69 | } | ||
70 | let len_before = buf.len(); | ||
71 | match component { | ||
72 | std::path::Component::Prefix(prefix) => { | ||
73 | // kind() returns a normalized and comparable path prefix. | ||
74 | prefix.kind().encode(buf); | ||
75 | } | ||
76 | std::path::Component::RootDir => { | ||
77 | if !add_sep { | ||
78 | component.as_os_str().encode(buf); | ||
79 | } | ||
80 | } | ||
81 | _ => component.as_os_str().encode(buf), | ||
82 | } | ||
83 | |||
84 | // some components may be encoded empty | ||
85 | add_sep = len_before != buf.len(); | ||
67 | } | 86 | } |
68 | } | 87 | } |
69 | #[cfg(unix)] | 88 | #[cfg(unix)] |
70 | { | 89 | { |
71 | use std::os::unix::ffi::OsStrExt; | 90 | use std::os::unix::ffi::OsStrExt; |
72 | buf.extend(path.as_bytes()); | 91 | buf.extend(path.as_os_str().as_bytes()); |
73 | } | 92 | } |
74 | #[cfg(not(any(windows, unix)))] | 93 | #[cfg(not(any(windows, unix)))] |
75 | { | 94 | { |
76 | buf.extend(path.to_string_lossy().as_bytes()); | 95 | buf.extend(path.as_os_str().to_string_lossy().as_bytes()); |
77 | } | 96 | } |
78 | } | 97 | } |
79 | VfsPathRepr::VirtualPath(VirtualPath(s)) => buf.extend(s.as_bytes()), | 98 | VfsPathRepr::VirtualPath(VirtualPath(s)) => buf.extend(s.as_bytes()), |
@@ -81,6 +100,112 @@ impl VfsPath { | |||
81 | } | 100 | } |
82 | } | 101 | } |
83 | 102 | ||
103 | #[cfg(windows)] | ||
104 | mod windows_paths { | ||
105 | pub trait Encode { | ||
106 | fn encode(&self, buf: &mut Vec<u8>); | ||
107 | } | ||
108 | |||
109 | impl Encode for std::ffi::OsStr { | ||
110 | fn encode(&self, buf: &mut Vec<u8>) { | ||
111 | use std::os::windows::ffi::OsStrExt; | ||
112 | for wchar in self.encode_wide() { | ||
113 | buf.extend(wchar.to_le_bytes().iter().copied()); | ||
114 | } | ||
115 | } | ||
116 | } | ||
117 | |||
118 | impl Encode for u8 { | ||
119 | fn encode(&self, buf: &mut Vec<u8>) { | ||
120 | let wide = *self as u16; | ||
121 | buf.extend(wide.to_le_bytes().iter().copied()) | ||
122 | } | ||
123 | } | ||
124 | |||
125 | impl Encode for &str { | ||
126 | fn encode(&self, buf: &mut Vec<u8>) { | ||
127 | debug_assert!(self.is_ascii()); | ||
128 | for b in self.as_bytes() { | ||
129 | b.encode(buf) | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | |||
134 | pub const SEP: &str = "\\"; | ||
135 | const VERBATIM: &str = "\\\\?\\"; | ||
136 | const UNC: &str = "UNC"; | ||
137 | const DEVICE: &str = "\\\\.\\"; | ||
138 | const COLON: &str = ":"; | ||
139 | |||
140 | impl Encode for std::path::Prefix<'_> { | ||
141 | fn encode(&self, buf: &mut Vec<u8>) { | ||
142 | match self { | ||
143 | std::path::Prefix::Verbatim(c) => { | ||
144 | VERBATIM.encode(buf); | ||
145 | c.encode(buf); | ||
146 | } | ||
147 | std::path::Prefix::VerbatimUNC(server, share) => { | ||
148 | VERBATIM.encode(buf); | ||
149 | UNC.encode(buf); | ||
150 | SEP.encode(buf); | ||
151 | server.encode(buf); | ||
152 | SEP.encode(buf); | ||
153 | share.encode(buf); | ||
154 | } | ||
155 | std::path::Prefix::VerbatimDisk(d) => { | ||
156 | VERBATIM.encode(buf); | ||
157 | d.encode(buf); | ||
158 | COLON.encode(buf); | ||
159 | } | ||
160 | std::path::Prefix::DeviceNS(device) => { | ||
161 | DEVICE.encode(buf); | ||
162 | device.encode(buf); | ||
163 | } | ||
164 | std::path::Prefix::UNC(server, share) => { | ||
165 | SEP.encode(buf); | ||
166 | SEP.encode(buf); | ||
167 | server.encode(buf); | ||
168 | SEP.encode(buf); | ||
169 | share.encode(buf); | ||
170 | } | ||
171 | std::path::Prefix::Disk(d) => { | ||
172 | d.encode(buf); | ||
173 | COLON.encode(buf); | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | #[test] | ||
179 | fn paths_encoding() { | ||
180 | // drive letter casing agnostic | ||
181 | test_eq("C:/x.rs", "c:/x.rs"); | ||
182 | // separator agnostic | ||
183 | test_eq("C:/x/y.rs", "C:\\x\\y.rs"); | ||
184 | |||
185 | fn test_eq(a: &str, b: &str) { | ||
186 | let mut b1 = Vec::new(); | ||
187 | let mut b2 = Vec::new(); | ||
188 | vfs(a).encode(&mut b1); | ||
189 | vfs(b).encode(&mut b2); | ||
190 | assert_eq!(b1, b2); | ||
191 | } | ||
192 | } | ||
193 | |||
194 | #[test] | ||
195 | fn test_sep_root_dir_encoding() { | ||
196 | let mut buf = Vec::new(); | ||
197 | vfs("C:/x/y").encode(&mut buf); | ||
198 | assert_eq!(&buf, &[0, 67, 0, 58, 0, 92, 0, 120, 0, 92, 0, 121, 0]) | ||
199 | } | ||
200 | |||
201 | #[cfg(test)] | ||
202 | fn vfs(str: &str) -> super::VfsPath { | ||
203 | use super::{AbsPathBuf, VfsPath}; | ||
204 | use std::convert::TryFrom; | ||
205 | VfsPath::from(AbsPathBuf::try_from(str).unwrap()) | ||
206 | } | ||
207 | } | ||
208 | |||
84 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 209 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
85 | enum VfsPathRepr { | 210 | enum VfsPathRepr { |
86 | PathBuf(AbsPathBuf), | 211 | PathBuf(AbsPathBuf), |