aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir_def/src/attr.rs23
-rw-r--r--crates/ra_hir_def/src/body.rs2
-rw-r--r--crates/ra_hir_def/src/body/lower.rs4
-rw-r--r--crates/ra_hir_def/src/find_path.rs78
-rw-r--r--crates/ra_hir_ty/src/tests.rs32
-rw-r--r--crates/ra_mbe/src/lib.rs50
-rw-r--r--crates/ra_mbe/src/tests.rs37
-rw-r--r--crates/rust-analyzer/src/config.rs6
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs37
9 files changed, 238 insertions, 31 deletions
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 714a66b02..5a86af8ba 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -13,7 +13,8 @@ use ra_syntax::{
13use tt::Subtree; 13use tt::Subtree;
14 14
15use crate::{ 15use crate::{
16 db::DefDatabase, path::ModPath, src::HasChildSource, src::HasSource, AdtId, AttrDefId, Lookup, 16 db::DefDatabase, nameres::ModuleSource, path::ModPath, src::HasChildSource, src::HasSource,
17 AdtId, AttrDefId, Lookup,
17}; 18};
18 19
19#[derive(Default, Debug, Clone, PartialEq, Eq)] 20#[derive(Default, Debug, Clone, PartialEq, Eq)]
@@ -37,11 +38,19 @@ impl Attrs {
37 match def { 38 match def {
38 AttrDefId::ModuleId(module) => { 39 AttrDefId::ModuleId(module) => {
39 let def_map = db.crate_def_map(module.krate); 40 let def_map = db.crate_def_map(module.krate);
40 let src = match def_map[module.local_id].declaration_source(db) { 41 let mod_data = &def_map[module.local_id];
41 Some(it) => it, 42 match mod_data.declaration_source(db) {
42 None => return Attrs::default(), 43 Some(it) => {
43 }; 44 Attrs::from_attrs_owner(db, it.as_ref().map(|it| it as &dyn AttrsOwner))
44 Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) 45 }
46 None => Attrs::from_attrs_owner(
47 db,
48 mod_data.definition_source(db).as_ref().map(|src| match src {
49 ModuleSource::SourceFile(file) => file as &dyn AttrsOwner,
50 ModuleSource::Module(module) => module as &dyn AttrsOwner,
51 }),
52 ),
53 }
45 } 54 }
46 AttrDefId::FieldId(it) => { 55 AttrDefId::FieldId(it) => {
47 let src = it.parent.child_source(db); 56 let src = it.parent.child_source(db);
@@ -106,7 +115,9 @@ pub struct Attr {
106 115
107#[derive(Debug, Clone, PartialEq, Eq)] 116#[derive(Debug, Clone, PartialEq, Eq)]
108pub enum AttrInput { 117pub enum AttrInput {
118 /// `#[attr = "string"]`
109 Literal(SmolStr), 119 Literal(SmolStr),
120 /// `#[attr(subtree)]`
110 TokenTree(Subtree), 121 TokenTree(Subtree),
111} 122}
112 123
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 3b169440a..890cefcaf 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -27,7 +27,7 @@ use crate::{
27 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, 27 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
28}; 28};
29 29
30/// A subser of Exander that only deals with cfg attributes. We only need it to 30/// A subset of Exander that only deals with cfg attributes. We only need it to
31/// avoid cyclic queries in crate def map during enum processing. 31/// avoid cyclic queries in crate def map during enum processing.
32pub(crate) struct CfgExpander { 32pub(crate) struct CfgExpander {
33 cfg_options: CfgOptions, 33 cfg_options: CfgOptions,
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 0caedd8d8..571603854 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -141,6 +141,10 @@ impl ExprCollector<'_> {
141 141
142 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { 142 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
143 let syntax_ptr = AstPtr::new(&expr); 143 let syntax_ptr = AstPtr::new(&expr);
144 let attrs = self.expander.parse_attrs(&expr);
145 if !self.expander.is_cfg_enabled(&attrs) {
146 return self.missing_expr();
147 }
144 match expr { 148 match expr {
145 ast::Expr::IfExpr(e) => { 149 ast::Expr::IfExpr(e) => {
146 let then_branch = self.collect_block_opt(e.then_branch()); 150 let then_branch = self.collect_block_opt(e.then_branch());
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index 81eff5bfe..70dcb03e6 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -19,7 +19,7 @@ impl ModPath {
19 19
20 // When std library is present, paths starting with `std::` 20 // When std library is present, paths starting with `std::`
21 // should be preferred over paths starting with `core::` and `alloc::` 21 // should be preferred over paths starting with `core::` and `alloc::`
22 fn should_start_with_std(&self) -> bool { 22 fn can_start_with_std(&self) -> bool {
23 self.segments 23 self.segments
24 .first() 24 .first()
25 .filter(|&first_segment| { 25 .filter(|&first_segment| {
@@ -132,6 +132,9 @@ fn find_path_inner(
132 } 132 }
133 133
134 // - otherwise, look for modules containing (reexporting) it and import it from one of those 134 // - otherwise, look for modules containing (reexporting) it and import it from one of those
135 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate };
136 let crate_attrs = db.attrs(crate_root.into());
137 let prefer_no_std = crate_attrs.by_key("no_std").exists();
135 let importable_locations = find_importable_locations(db, item, from); 138 let importable_locations = find_importable_locations(db, item, from);
136 let mut best_path = None; 139 let mut best_path = None;
137 let mut best_path_len = max_len; 140 let mut best_path_len = max_len;
@@ -147,21 +150,32 @@ fn find_path_inner(
147 }; 150 };
148 path.segments.push(name); 151 path.segments.push(name);
149 152
150 let new_path = 153 let new_path = if let Some(best_path) = best_path {
151 if let Some(best_path) = best_path { select_best_path(best_path, path) } else { path }; 154 select_best_path(best_path, path, prefer_no_std)
155 } else {
156 path
157 };
152 best_path_len = new_path.len(); 158 best_path_len = new_path.len();
153 best_path = Some(new_path); 159 best_path = Some(new_path);
154 } 160 }
155 best_path 161 best_path
156} 162}
157 163
158fn select_best_path(old_path: ModPath, new_path: ModPath) -> ModPath { 164fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
159 if old_path.starts_with_std() && new_path.should_start_with_std() { 165 if old_path.starts_with_std() && new_path.can_start_with_std() {
160 tested_by!(prefer_std_paths); 166 tested_by!(prefer_std_paths);
161 old_path 167 if prefer_no_std {
162 } else if new_path.starts_with_std() && old_path.should_start_with_std() { 168 new_path
169 } else {
170 old_path
171 }
172 } else if new_path.starts_with_std() && old_path.can_start_with_std() {
163 tested_by!(prefer_std_paths); 173 tested_by!(prefer_std_paths);
164 new_path 174 if prefer_no_std {
175 old_path
176 } else {
177 new_path
178 }
165 } else if new_path.len() < old_path.len() { 179 } else if new_path.len() < old_path.len() {
166 new_path 180 new_path
167 } else { 181 } else {
@@ -513,6 +527,54 @@ mod tests {
513 } 527 }
514 528
515 #[test] 529 #[test]
530 fn prefer_alloc_paths_over_std() {
531 covers!(prefer_std_paths);
532 let code = r#"
533 //- /main.rs crate:main deps:alloc,std
534 #![no_std]
535
536 <|>
537
538 //- /std.rs crate:std deps:alloc
539
540 pub mod sync {
541 pub use alloc::sync::Arc;
542 }
543
544 //- /zzz.rs crate:alloc
545
546 pub mod sync {
547 pub struct Arc;
548 }
549 "#;
550 check_found_path(code, "alloc::sync::Arc");
551 }
552
553 #[test]
554 fn prefer_core_paths_over_std() {
555 covers!(prefer_std_paths);
556 let code = r#"
557 //- /main.rs crate:main deps:core,std
558 #![no_std]
559
560 <|>
561
562 //- /std.rs crate:std deps:core
563
564 pub mod fmt {
565 pub use core::fmt::Error;
566 }
567
568 //- /zzz.rs crate:core
569
570 pub mod fmt {
571 pub struct Error;
572 }
573 "#;
574 check_found_path(code, "core::fmt::Error");
575 }
576
577 #[test]
516 fn prefer_shorter_paths_if_not_alloc() { 578 fn prefer_shorter_paths_if_not_alloc() {
517 let code = r#" 579 let code = r#"
518 //- /main.rs crate:main deps:megaalloc,std 580 //- /main.rs crate:main deps:megaalloc,std
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index b6a96bb5c..588d81282 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -391,6 +391,38 @@ fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
391} 391}
392 392
393#[test] 393#[test]
394fn no_such_field_with_feature_flag_diagnostics_on_block_expr() {
395 let diagnostics = TestDB::with_files(
396 r#"
397 //- /lib.rs crate:foo cfg:feature=foo
398 struct S {
399 #[cfg(feature = "foo")]
400 foo: u32,
401 #[cfg(not(feature = "foo"))]
402 bar: u32,
403 }
404
405 impl S {
406 fn new(bar: u32) -> Self {
407 #[cfg(feature = "foo")]
408 {
409 Self { foo: bar }
410 }
411 #[cfg(not(feature = "foo"))]
412 {
413 Self { bar }
414 }
415 }
416 }
417 "#,
418 )
419 .diagnostics()
420 .0;
421
422 assert_snapshot!(diagnostics, @r###""###);
423}
424
425#[test]
394fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() { 426fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() {
395 let diagnostics = TestDB::with_files( 427 let diagnostics = TestDB::with_files(
396 r#" 428 r#"
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs
index 535b7daa0..1a020398e 100644
--- a/crates/ra_mbe/src/lib.rs
+++ b/crates/ra_mbe/src/lib.rs
@@ -19,6 +19,7 @@ use crate::{
19#[derive(Debug, PartialEq, Eq)] 19#[derive(Debug, PartialEq, Eq)]
20pub enum ParseError { 20pub enum ParseError {
21 Expected(String), 21 Expected(String),
22 RepetitionEmtpyTokenTree,
22} 23}
23 24
24#[derive(Debug, PartialEq, Eq)] 25#[derive(Debug, PartialEq, Eq)]
@@ -194,20 +195,46 @@ impl Rule {
194 } 195 }
195} 196}
196 197
198fn to_parse_error(e: ExpandError) -> ParseError {
199 let msg = match e {
200 ExpandError::InvalidRepeat => "invalid repeat".to_string(),
201 _ => "invalid macro definition".to_string(),
202 };
203 ParseError::Expected(msg)
204}
205
197fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> { 206fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> {
198 for op in parse_pattern(pattern) { 207 for op in parse_pattern(pattern) {
199 let op = match op { 208 let op = op.map_err(to_parse_error)?;
200 Ok(it) => it, 209
201 Err(e) => {
202 let msg = match e {
203 ExpandError::InvalidRepeat => "invalid repeat".to_string(),
204 _ => "invalid macro definition".to_string(),
205 };
206 return Err(ParseError::Expected(msg));
207 }
208 };
209 match op { 210 match op {
210 Op::TokenTree(tt::TokenTree::Subtree(subtree)) | Op::Repeat { subtree, .. } => { 211 Op::TokenTree(tt::TokenTree::Subtree(subtree)) => validate(subtree)?,
212 Op::Repeat { subtree, separator, .. } => {
213 // Checks that no repetition which could match an empty token
214 // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558
215
216 if separator.is_none() {
217 if parse_pattern(subtree).all(|child_op| {
218 match child_op.map_err(to_parse_error) {
219 Ok(Op::Var { kind, .. }) => {
220 // vis is optional
221 if kind.map_or(false, |it| it == "vis") {
222 return true;
223 }
224 }
225 Ok(Op::Repeat { kind, .. }) => {
226 return matches!(
227 kind,
228 parser::RepeatKind::ZeroOrMore | parser::RepeatKind::ZeroOrOne
229 )
230 }
231 _ => {}
232 }
233 false
234 }) {
235 return Err(ParseError::RepetitionEmtpyTokenTree);
236 }
237 }
211 validate(subtree)? 238 validate(subtree)?
212 } 239 }
213 _ => (), 240 _ => (),
@@ -216,6 +243,7 @@ fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> {
216 Ok(()) 243 Ok(())
217} 244}
218 245
246#[derive(Debug)]
219pub struct ExpandResult<T>(pub T, pub Option<ExpandError>); 247pub struct ExpandResult<T>(pub T, pub Option<ExpandError>);
220 248
221impl<T> ExpandResult<T> { 249impl<T> ExpandResult<T> {
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs
index 7ceec7752..0d924ce58 100644
--- a/crates/ra_mbe/src/tests.rs
+++ b/crates/ra_mbe/src/tests.rs
@@ -1657,7 +1657,7 @@ impl MacroFixture {
1657 } 1657 }
1658} 1658}
1659 1659
1660pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { 1660fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree {
1661 let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); 1661 let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
1662 let macro_definition = 1662 let macro_definition =
1663 source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); 1663 source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
@@ -1671,10 +1671,24 @@ pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture {
1671 .0; 1671 .0;
1672 assert_eq!(definition_tt, parsed); 1672 assert_eq!(definition_tt, parsed);
1673 1673
1674 definition_tt
1675}
1676
1677pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture {
1678 let definition_tt = parse_macro_to_tt(ra_fixture);
1674 let rules = MacroRules::parse(&definition_tt).unwrap(); 1679 let rules = MacroRules::parse(&definition_tt).unwrap();
1675 MacroFixture { rules } 1680 MacroFixture { rules }
1676} 1681}
1677 1682
1683pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError {
1684 let definition_tt = parse_macro_to_tt(ra_fixture);
1685
1686 match MacroRules::parse(&definition_tt) {
1687 Ok(_) => panic!("Expect error"),
1688 Err(err) => err,
1689 }
1690}
1691
1678pub(crate) fn parse_to_token_tree_by_syntax(ra_fixture: &str) -> tt::Subtree { 1692pub(crate) fn parse_to_token_tree_by_syntax(ra_fixture: &str) -> tt::Subtree {
1679 let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); 1693 let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
1680 let tt = syntax_node_to_token_tree(source_file.syntax()).unwrap().0; 1694 let tt = syntax_node_to_token_tree(source_file.syntax()).unwrap().0;
@@ -1840,6 +1854,27 @@ fn test_no_space_after_semi_colon() {
1840 ); 1854 );
1841} 1855}
1842 1856
1857// https://github.com/rust-lang/rust/blob/master/src/test/ui/issues/issue-57597.rs
1858#[test]
1859fn test_rustc_issue_57597() {
1860 fn test_error(fixture: &str) {
1861 assert_eq!(parse_macro_error(fixture), ParseError::RepetitionEmtpyTokenTree);
1862 }
1863
1864 test_error("macro_rules! foo { ($($($i:ident)?)+) => {}; }");
1865 test_error("macro_rules! foo { ($($($i:ident)?)*) => {}; }");
1866 test_error("macro_rules! foo { ($($($i:ident)?)?) => {}; }");
1867 test_error("macro_rules! foo { ($($($($i:ident)?)?)?) => {}; }");
1868 test_error("macro_rules! foo { ($($($($i:ident)*)?)?) => {}; }");
1869 test_error("macro_rules! foo { ($($($($i:ident)?)*)?) => {}; }");
1870 test_error("macro_rules! foo { ($($($($i:ident)?)?)*) => {}; }");
1871 test_error("macro_rules! foo { ($($($($i:ident)*)*)?) => {}; }");
1872 test_error("macro_rules! foo { ($($($($i:ident)?)*)*) => {}; }");
1873 test_error("macro_rules! foo { ($($($($i:ident)?)*)+) => {}; }");
1874 test_error("macro_rules! foo { ($($($($i:ident)+)?)*) => {}; }");
1875 test_error("macro_rules! foo { ($($($($i:ident)+)*)?) => {}; }");
1876}
1877
1843#[test] 1878#[test]
1844fn test_expand_bad_literal() { 1879fn test_expand_bad_literal() {
1845 parse_macro( 1880 parse_macro(
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 715eddadb..74a63e32a 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -69,6 +69,7 @@ pub enum RustfmtConfig {
69pub struct ClientCapsConfig { 69pub struct ClientCapsConfig {
70 pub location_link: bool, 70 pub location_link: bool,
71 pub line_folding_only: bool, 71 pub line_folding_only: bool,
72 pub hierarchical_symbols: bool,
72} 73}
73 74
74impl Default for Config { 75impl Default for Config {
@@ -215,6 +216,11 @@ impl Config {
215 if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) { 216 if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) {
216 self.client_caps.line_folding_only = value 217 self.client_caps.line_folding_only = value
217 } 218 }
219 if let Some(value) =
220 caps.document_symbol.as_ref().and_then(|it| it.hierarchical_document_symbol_support)
221 {
222 self.client_caps.hierarchical_symbols = value
223 }
218 self.completion.allow_snippets(false); 224 self.completion.allow_snippets(false);
219 if let Some(completion) = &caps.completion { 225 if let Some(completion) = &caps.completion {
220 if let Some(completion_item) = &completion.completion_item { 226 if let Some(completion_item) = &completion.completion_item {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index c2c1a23cd..e87e8db5d 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -16,7 +16,7 @@ use lsp_types::{
16 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, 16 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse,
17 Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams, 17 Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams,
18 SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, 18 SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier,
19 TextEdit, WorkspaceEdit, 19 TextEdit, Url, WorkspaceEdit,
20}; 20};
21use ra_ide::{ 21use ra_ide::{
22 Assist, AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, 22 Assist, AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
@@ -219,6 +219,7 @@ pub fn handle_document_symbol(
219 let _p = profile("handle_document_symbol"); 219 let _p = profile("handle_document_symbol");
220 let file_id = params.text_document.try_conv_with(&world)?; 220 let file_id = params.text_document.try_conv_with(&world)?;
221 let line_index = world.analysis().file_line_index(file_id)?; 221 let line_index = world.analysis().file_line_index(file_id)?;
222 let url = file_id.try_conv_with(&world)?;
222 223
223 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); 224 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
224 225
@@ -234,10 +235,10 @@ pub fn handle_document_symbol(
234 }; 235 };
235 parents.push((doc_symbol, symbol.parent)); 236 parents.push((doc_symbol, symbol.parent));
236 } 237 }
237 let mut res = Vec::new(); 238 let mut document_symbols = Vec::new();
238 while let Some((node, parent)) = parents.pop() { 239 while let Some((node, parent)) = parents.pop() {
239 match parent { 240 match parent {
240 None => res.push(node), 241 None => document_symbols.push(node),
241 Some(i) => { 242 Some(i) => {
242 let children = &mut parents[i].0.children; 243 let children = &mut parents[i].0.children;
243 if children.is_none() { 244 if children.is_none() {
@@ -248,7 +249,35 @@ pub fn handle_document_symbol(
248 } 249 }
249 } 250 }
250 251
251 Ok(Some(res.into())) 252 if world.config.client_caps.hierarchical_symbols {
253 Ok(Some(document_symbols.into()))
254 } else {
255 let mut symbol_information = Vec::<SymbolInformation>::new();
256 for symbol in document_symbols {
257 flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
258 }
259
260 Ok(Some(symbol_information.into()))
261 }
262}
263
264fn flatten_document_symbol(
265 symbol: &DocumentSymbol,
266 container_name: Option<String>,
267 url: &Url,
268 res: &mut Vec<SymbolInformation>,
269) {
270 res.push(SymbolInformation {
271 name: symbol.name.clone(),
272 kind: symbol.kind,
273 deprecated: symbol.deprecated,
274 location: Location::new(url.clone(), symbol.range),
275 container_name: container_name,
276 });
277
278 for child in symbol.children.iter().flatten() {
279 flatten_document_symbol(child, Some(symbol.name.clone()), url, res);
280 }
252} 281}
253 282
254pub fn handle_workspace_symbol( 283pub fn handle_workspace_symbol(