diff options
Diffstat (limited to 'crates')
53 files changed, 1348 insertions, 544 deletions
diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/assists/apply_demorgan.rs new file mode 100644 index 000000000..5f2b0dd18 --- /dev/null +++ b/crates/ra_assists/src/assists/apply_demorgan.rs | |||
@@ -0,0 +1,102 @@ | |||
1 | //! This contains the functions associated with the demorgan assist. | ||
2 | //! This assist transforms boolean expressions of the form `!a || !b` into | ||
3 | //! `!(a && b)`. | ||
4 | use hir::db::HirDatabase; | ||
5 | use ra_syntax::ast::{self, AstNode}; | ||
6 | use ra_syntax::SyntaxNode; | ||
7 | |||
8 | use crate::{Assist, AssistCtx, AssistId}; | ||
9 | |||
10 | /// Assist for applying demorgan's law | ||
11 | /// | ||
12 | /// This transforms expressions of the form `!l || !r` into `!(l && r)`. | ||
13 | /// This also works with `&&`. This assist can only be applied with the cursor | ||
14 | /// on either `||` or `&&`, with both operands being a negation of some kind. | ||
15 | /// This means something of the form `!x` or `x != y`. | ||
16 | pub(crate) fn apply_demorgan(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
17 | let expr = ctx.node_at_offset::<ast::BinExpr>()?; | ||
18 | let op = expr.op_kind()?; | ||
19 | let op_range = expr.op_token()?.text_range(); | ||
20 | let opposite_op = opposite_logic_op(op)?; | ||
21 | let cursor_in_range = ctx.frange.range.is_subrange(&op_range); | ||
22 | if !cursor_in_range { | ||
23 | return None; | ||
24 | } | ||
25 | let lhs = expr.lhs()?.syntax().clone(); | ||
26 | let lhs_range = lhs.text_range(); | ||
27 | let rhs = expr.rhs()?.syntax().clone(); | ||
28 | let rhs_range = rhs.text_range(); | ||
29 | let not_lhs = undo_negation(lhs)?; | ||
30 | let not_rhs = undo_negation(rhs)?; | ||
31 | |||
32 | ctx.add_action(AssistId("apply_demorgan"), "apply demorgan's law", |edit| { | ||
33 | edit.target(op_range); | ||
34 | edit.replace(op_range, opposite_op); | ||
35 | edit.replace(lhs_range, format!("!({}", not_lhs)); | ||
36 | edit.replace(rhs_range, format!("{})", not_rhs)); | ||
37 | }); | ||
38 | ctx.build() | ||
39 | } | ||
40 | |||
41 | // Return the opposite text for a given logical operator, if it makes sense | ||
42 | fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> { | ||
43 | match kind { | ||
44 | ast::BinOp::BooleanOr => Some("&&"), | ||
45 | ast::BinOp::BooleanAnd => Some("||"), | ||
46 | _ => None, | ||
47 | } | ||
48 | } | ||
49 | |||
50 | // This function tries to undo unary negation, or inequality | ||
51 | fn undo_negation(node: SyntaxNode) -> Option<String> { | ||
52 | match ast::Expr::cast(node)? { | ||
53 | ast::Expr::BinExpr(bin) => match bin.op_kind()? { | ||
54 | ast::BinOp::NegatedEqualityTest => { | ||
55 | let lhs = bin.lhs()?.syntax().text(); | ||
56 | let rhs = bin.rhs()?.syntax().text(); | ||
57 | Some(format!("{} == {}", lhs, rhs)) | ||
58 | } | ||
59 | _ => None, | ||
60 | }, | ||
61 | ast::Expr::PrefixExpr(pe) => match pe.op_kind()? { | ||
62 | ast::PrefixOp::Not => { | ||
63 | let child = pe.expr()?.syntax().text(); | ||
64 | Some(String::from(child)) | ||
65 | } | ||
66 | _ => None, | ||
67 | }, | ||
68 | _ => None, | ||
69 | } | ||
70 | } | ||
71 | |||
72 | #[cfg(test)] | ||
73 | mod tests { | ||
74 | use super::*; | ||
75 | |||
76 | use crate::helpers::{check_assist, check_assist_not_applicable}; | ||
77 | |||
78 | #[test] | ||
79 | fn demorgan_turns_and_into_or() { | ||
80 | check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x ||<|> x) }") | ||
81 | } | ||
82 | |||
83 | #[test] | ||
84 | fn demorgan_turns_or_into_and() { | ||
85 | check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x &&<|> x) }") | ||
86 | } | ||
87 | |||
88 | #[test] | ||
89 | fn demorgan_removes_inequality() { | ||
90 | check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x &&<|> x) }") | ||
91 | } | ||
92 | |||
93 | #[test] | ||
94 | fn demorgan_doesnt_apply_with_cursor_not_on_op() { | ||
95 | check_assist_not_applicable(apply_demorgan, "fn f() { <|> !x || !x }") | ||
96 | } | ||
97 | |||
98 | #[test] | ||
99 | fn demorgan_doesnt_apply_when_operands_arent_negated_already() { | ||
100 | check_assist_not_applicable(apply_demorgan, "fn f() { x ||<|> x }") | ||
101 | } | ||
102 | } | ||
diff --git a/crates/ra_assists/src/assists/split_import.rs b/crates/ra_assists/src/assists/split_import.rs index 19d429daf..fe3e64af5 100644 --- a/crates/ra_assists/src/assists/split_import.rs +++ b/crates/ra_assists/src/assists/split_import.rs | |||
@@ -51,13 +51,13 @@ mod tests { | |||
51 | fn split_import_works_with_trees() { | 51 | fn split_import_works_with_trees() { |
52 | check_assist( | 52 | check_assist( |
53 | split_import, | 53 | split_import, |
54 | "use algo:<|>:visitor::{Visitor, visit}", | 54 | "use crate:<|>:db::{RootDatabase, FileSymbol}", |
55 | "use algo::{<|>visitor::{Visitor, visit}}", | 55 | "use crate::{<|>db::{RootDatabase, FileSymbol}}", |
56 | ) | 56 | ) |
57 | } | 57 | } |
58 | 58 | ||
59 | #[test] | 59 | #[test] |
60 | fn split_import_target() { | 60 | fn split_import_target() { |
61 | check_assist_target(split_import, "use algo::<|>visitor::{Visitor, visit}", "::"); | 61 | check_assist_target(split_import, "use crate::<|>db::{RootDatabase, FileSymbol}", "::"); |
62 | } | 62 | } |
63 | } | 63 | } |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 91b2a1dce..d2376c475 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -92,6 +92,7 @@ mod assists { | |||
92 | mod add_derive; | 92 | mod add_derive; |
93 | mod add_explicit_type; | 93 | mod add_explicit_type; |
94 | mod add_impl; | 94 | mod add_impl; |
95 | mod apply_demorgan; | ||
95 | mod flip_comma; | 96 | mod flip_comma; |
96 | mod flip_binexpr; | 97 | mod flip_binexpr; |
97 | mod change_visibility; | 98 | mod change_visibility; |
@@ -113,6 +114,7 @@ mod assists { | |||
113 | add_derive::add_derive, | 114 | add_derive::add_derive, |
114 | add_explicit_type::add_explicit_type, | 115 | add_explicit_type::add_explicit_type, |
115 | add_impl::add_impl, | 116 | add_impl::add_impl, |
117 | apply_demorgan::apply_demorgan, | ||
116 | change_visibility::change_visibility, | 118 | change_visibility::change_visibility, |
117 | fill_match_arms::fill_match_arms, | 119 | fill_match_arms::fill_match_arms, |
118 | merge_match_arms::merge_match_arms, | 120 | merge_match_arms::merge_match_arms, |
diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs index 939f72037..a5fc2a23e 100644 --- a/crates/ra_batch/src/lib.rs +++ b/crates/ra_batch/src/lib.rs | |||
@@ -7,7 +7,7 @@ use rustc_hash::FxHashMap; | |||
7 | use crossbeam_channel::{unbounded, Receiver}; | 7 | use crossbeam_channel::{unbounded, Receiver}; |
8 | use ra_db::{CrateGraph, FileId, SourceRootId}; | 8 | use ra_db::{CrateGraph, FileId, SourceRootId}; |
9 | use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags}; | 9 | use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags}; |
10 | use ra_project_model::{PackageRoot, ProjectWorkspace}; | 10 | use ra_project_model::{get_rustc_cfg_options, PackageRoot, ProjectWorkspace}; |
11 | use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; | 11 | use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; |
12 | use ra_vfs_glob::RustPackageFilterBuilder; | 12 | use ra_vfs_glob::RustPackageFilterBuilder; |
13 | 13 | ||
@@ -41,11 +41,17 @@ pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap<SourceRootId, | |||
41 | sender, | 41 | sender, |
42 | Watch(false), | 42 | Watch(false), |
43 | ); | 43 | ); |
44 | let (crate_graph, _crate_names) = ws.to_crate_graph(&mut |path: &Path| { | 44 | |
45 | let vfs_file = vfs.load(path); | 45 | // FIXME: cfg options? |
46 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); | 46 | let default_cfg_options = |
47 | vfs_file.map(vfs_file_to_id) | 47 | get_rustc_cfg_options().atom("test".into()).atom("debug_assertion".into()); |
48 | }); | 48 | |
49 | let (crate_graph, _crate_names) = | ||
50 | ws.to_crate_graph(&default_cfg_options, &mut |path: &Path| { | ||
51 | let vfs_file = vfs.load(path); | ||
52 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); | ||
53 | vfs_file.map(vfs_file_to_id) | ||
54 | }); | ||
49 | log::debug!("crate graph: {:?}", crate_graph); | 55 | log::debug!("crate graph: {:?}", crate_graph); |
50 | 56 | ||
51 | let source_roots = roots | 57 | let source_roots = roots |
diff --git a/crates/ra_cfg/Cargo.toml b/crates/ra_cfg/Cargo.toml new file mode 100644 index 000000000..b28affc3a --- /dev/null +++ b/crates/ra_cfg/Cargo.toml | |||
@@ -0,0 +1,14 @@ | |||
1 | [package] | ||
2 | edition = "2018" | ||
3 | name = "ra_cfg" | ||
4 | version = "0.1.0" | ||
5 | authors = ["rust-analyzer developers"] | ||
6 | |||
7 | [dependencies] | ||
8 | rustc-hash = "1.0.1" | ||
9 | |||
10 | ra_syntax = { path = "../ra_syntax" } | ||
11 | tt = { path = "../ra_tt", package = "ra_tt" } | ||
12 | |||
13 | [dev-dependencies] | ||
14 | mbe = { path = "../ra_mbe", package = "ra_mbe" } | ||
diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs new file mode 100644 index 000000000..39d71851c --- /dev/null +++ b/crates/ra_cfg/src/cfg_expr.rs | |||
@@ -0,0 +1,132 @@ | |||
1 | //! The condition expression used in `#[cfg(..)]` attributes. | ||
2 | //! | ||
3 | //! See: https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation | ||
4 | |||
5 | use std::slice::Iter as SliceIter; | ||
6 | |||
7 | use ra_syntax::SmolStr; | ||
8 | use tt::{Leaf, Subtree, TokenTree}; | ||
9 | |||
10 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
11 | pub enum CfgExpr { | ||
12 | Invalid, | ||
13 | Atom(SmolStr), | ||
14 | KeyValue { key: SmolStr, value: SmolStr }, | ||
15 | All(Vec<CfgExpr>), | ||
16 | Any(Vec<CfgExpr>), | ||
17 | Not(Box<CfgExpr>), | ||
18 | } | ||
19 | |||
20 | impl CfgExpr { | ||
21 | /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. | ||
22 | pub fn fold(&self, query: &dyn Fn(&SmolStr, Option<&SmolStr>) -> bool) -> Option<bool> { | ||
23 | match self { | ||
24 | CfgExpr::Invalid => None, | ||
25 | CfgExpr::Atom(name) => Some(query(name, None)), | ||
26 | CfgExpr::KeyValue { key, value } => Some(query(key, Some(value))), | ||
27 | CfgExpr::All(preds) => { | ||
28 | preds.iter().try_fold(true, |s, pred| Some(s && pred.fold(query)?)) | ||
29 | } | ||
30 | CfgExpr::Any(preds) => { | ||
31 | preds.iter().try_fold(false, |s, pred| Some(s || pred.fold(query)?)) | ||
32 | } | ||
33 | CfgExpr::Not(pred) => pred.fold(query).map(|s| !s), | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | |||
38 | pub fn parse_cfg(tt: &Subtree) -> CfgExpr { | ||
39 | next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) | ||
40 | } | ||
41 | |||
42 | fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> { | ||
43 | let name = match it.next() { | ||
44 | None => return None, | ||
45 | Some(TokenTree::Leaf(Leaf::Ident(ident))) => ident.text.clone(), | ||
46 | Some(_) => return Some(CfgExpr::Invalid), | ||
47 | }; | ||
48 | |||
49 | // Peek | ||
50 | let ret = match it.as_slice().first() { | ||
51 | Some(TokenTree::Leaf(Leaf::Punct(punct))) if punct.char == '=' => { | ||
52 | match it.as_slice().get(1) { | ||
53 | Some(TokenTree::Leaf(Leaf::Literal(literal))) => { | ||
54 | it.next(); | ||
55 | it.next(); | ||
56 | // FIXME: escape? raw string? | ||
57 | let value = | ||
58 | SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); | ||
59 | CfgExpr::KeyValue { key: name, value } | ||
60 | } | ||
61 | _ => return Some(CfgExpr::Invalid), | ||
62 | } | ||
63 | } | ||
64 | Some(TokenTree::Subtree(subtree)) => { | ||
65 | it.next(); | ||
66 | let mut sub_it = subtree.token_trees.iter(); | ||
67 | let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)).collect(); | ||
68 | match name.as_str() { | ||
69 | "all" => CfgExpr::All(subs), | ||
70 | "any" => CfgExpr::Any(subs), | ||
71 | "not" => CfgExpr::Not(Box::new(subs.pop().unwrap_or(CfgExpr::Invalid))), | ||
72 | _ => CfgExpr::Invalid, | ||
73 | } | ||
74 | } | ||
75 | _ => CfgExpr::Atom(name), | ||
76 | }; | ||
77 | |||
78 | // Eat comma separator | ||
79 | if let Some(TokenTree::Leaf(Leaf::Punct(punct))) = it.as_slice().first() { | ||
80 | if punct.char == ',' { | ||
81 | it.next(); | ||
82 | } | ||
83 | } | ||
84 | Some(ret) | ||
85 | } | ||
86 | |||
87 | #[cfg(test)] | ||
88 | mod tests { | ||
89 | use super::*; | ||
90 | |||
91 | use mbe::ast_to_token_tree; | ||
92 | use ra_syntax::ast::{self, AstNode}; | ||
93 | |||
94 | fn assert_parse_result(input: &str, expected: CfgExpr) { | ||
95 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
96 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
97 | let (tt, _) = ast_to_token_tree(&tt).unwrap(); | ||
98 | assert_eq!(parse_cfg(&tt), expected); | ||
99 | } | ||
100 | |||
101 | #[test] | ||
102 | fn test_cfg_expr_parser() { | ||
103 | assert_parse_result("#![cfg(foo)]", CfgExpr::Atom("foo".into())); | ||
104 | assert_parse_result("#![cfg(foo,)]", CfgExpr::Atom("foo".into())); | ||
105 | assert_parse_result( | ||
106 | "#![cfg(not(foo))]", | ||
107 | CfgExpr::Not(Box::new(CfgExpr::Atom("foo".into()))), | ||
108 | ); | ||
109 | assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid); | ||
110 | |||
111 | // Only take the first | ||
112 | assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgExpr::Atom("foo".into())); | ||
113 | |||
114 | assert_parse_result( | ||
115 | r#"#![cfg(all(foo, bar = "baz"))]"#, | ||
116 | CfgExpr::All(vec![ | ||
117 | CfgExpr::Atom("foo".into()), | ||
118 | CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() }, | ||
119 | ]), | ||
120 | ); | ||
121 | |||
122 | assert_parse_result( | ||
123 | r#"#![cfg(any(not(), all(), , bar = "baz",))]"#, | ||
124 | CfgExpr::Any(vec![ | ||
125 | CfgExpr::Not(Box::new(CfgExpr::Invalid)), | ||
126 | CfgExpr::All(vec![]), | ||
127 | CfgExpr::Invalid, | ||
128 | CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() }, | ||
129 | ]), | ||
130 | ); | ||
131 | } | ||
132 | } | ||
diff --git a/crates/ra_cfg/src/lib.rs b/crates/ra_cfg/src/lib.rs new file mode 100644 index 000000000..e1c92fbba --- /dev/null +++ b/crates/ra_cfg/src/lib.rs | |||
@@ -0,0 +1,61 @@ | |||
1 | //! ra_cfg defines conditional compiling options, `cfg` attibute parser and evaluator | ||
2 | use std::iter::IntoIterator; | ||
3 | |||
4 | use ra_syntax::SmolStr; | ||
5 | use rustc_hash::FxHashSet; | ||
6 | |||
7 | mod cfg_expr; | ||
8 | |||
9 | pub use cfg_expr::{parse_cfg, CfgExpr}; | ||
10 | |||
11 | /// Configuration options used for conditional compilition on items with `cfg` attributes. | ||
12 | /// We have two kind of options in different namespaces: atomic options like `unix`, and | ||
13 | /// key-value options like `target_arch="x86"`. | ||
14 | /// | ||
15 | /// Note that for key-value options, one key can have multiple values (but not none). | ||
16 | /// `feature` is an example. We have both `feature="foo"` and `feature="bar"` if features | ||
17 | /// `foo` and `bar` are both enabled. And here, we store key-value options as a set of tuple | ||
18 | /// of key and value in `key_values`. | ||
19 | /// | ||
20 | /// See: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options | ||
21 | #[derive(Debug, Clone, PartialEq, Eq, Default)] | ||
22 | pub struct CfgOptions { | ||
23 | atoms: FxHashSet<SmolStr>, | ||
24 | key_values: FxHashSet<(SmolStr, SmolStr)>, | ||
25 | } | ||
26 | |||
27 | impl CfgOptions { | ||
28 | pub fn check(&self, cfg: &CfgExpr) -> Option<bool> { | ||
29 | cfg.fold(&|key, value| match value { | ||
30 | None => self.atoms.contains(key), | ||
31 | Some(value) => self.key_values.contains(&(key.clone(), value.clone())), | ||
32 | }) | ||
33 | } | ||
34 | |||
35 | pub fn is_cfg_enabled(&self, attr: &tt::Subtree) -> Option<bool> { | ||
36 | self.check(&parse_cfg(attr)) | ||
37 | } | ||
38 | |||
39 | pub fn atom(mut self, name: SmolStr) -> CfgOptions { | ||
40 | self.atoms.insert(name); | ||
41 | self | ||
42 | } | ||
43 | |||
44 | pub fn key_value(mut self, key: SmolStr, value: SmolStr) -> CfgOptions { | ||
45 | self.key_values.insert((key, value)); | ||
46 | self | ||
47 | } | ||
48 | |||
49 | /// Shortcut to set features | ||
50 | pub fn features(mut self, iter: impl IntoIterator<Item = SmolStr>) -> CfgOptions { | ||
51 | for feat in iter { | ||
52 | self = self.key_value("feature".into(), feat); | ||
53 | } | ||
54 | self | ||
55 | } | ||
56 | |||
57 | pub fn remove_atom(mut self, name: &SmolStr) -> CfgOptions { | ||
58 | self.atoms.remove(name); | ||
59 | self | ||
60 | } | ||
61 | } | ||
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index 2fac07bc5..c141f1a88 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml | |||
@@ -10,4 +10,5 @@ relative-path = "0.4.0" | |||
10 | rustc-hash = "1.0" | 10 | rustc-hash = "1.0" |
11 | 11 | ||
12 | ra_syntax = { path = "../ra_syntax" } | 12 | ra_syntax = { path = "../ra_syntax" } |
13 | ra_cfg = { path = "../ra_cfg" } | ||
13 | ra_prof = { path = "../ra_prof" } | 14 | ra_prof = { path = "../ra_prof" } |
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 52f892891..23148096c 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -9,6 +9,7 @@ | |||
9 | use relative_path::{RelativePath, RelativePathBuf}; | 9 | use relative_path::{RelativePath, RelativePathBuf}; |
10 | use rustc_hash::FxHashMap; | 10 | use rustc_hash::FxHashMap; |
11 | 11 | ||
12 | use ra_cfg::CfgOptions; | ||
12 | use ra_syntax::SmolStr; | 13 | use ra_syntax::SmolStr; |
13 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
14 | 15 | ||
@@ -109,11 +110,12 @@ struct CrateData { | |||
109 | file_id: FileId, | 110 | file_id: FileId, |
110 | edition: Edition, | 111 | edition: Edition, |
111 | dependencies: Vec<Dependency>, | 112 | dependencies: Vec<Dependency>, |
113 | cfg_options: CfgOptions, | ||
112 | } | 114 | } |
113 | 115 | ||
114 | impl CrateData { | 116 | impl CrateData { |
115 | fn new(file_id: FileId, edition: Edition) -> CrateData { | 117 | fn new(file_id: FileId, edition: Edition, cfg_options: CfgOptions) -> CrateData { |
116 | CrateData { file_id, edition, dependencies: Vec::new() } | 118 | CrateData { file_id, edition, dependencies: Vec::new(), cfg_options } |
117 | } | 119 | } |
118 | 120 | ||
119 | fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { | 121 | fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { |
@@ -134,13 +136,22 @@ impl Dependency { | |||
134 | } | 136 | } |
135 | 137 | ||
136 | impl CrateGraph { | 138 | impl CrateGraph { |
137 | pub fn add_crate_root(&mut self, file_id: FileId, edition: Edition) -> CrateId { | 139 | pub fn add_crate_root( |
140 | &mut self, | ||
141 | file_id: FileId, | ||
142 | edition: Edition, | ||
143 | cfg_options: CfgOptions, | ||
144 | ) -> CrateId { | ||
138 | let crate_id = CrateId(self.arena.len() as u32); | 145 | let crate_id = CrateId(self.arena.len() as u32); |
139 | let prev = self.arena.insert(crate_id, CrateData::new(file_id, edition)); | 146 | let prev = self.arena.insert(crate_id, CrateData::new(file_id, edition, cfg_options)); |
140 | assert!(prev.is_none()); | 147 | assert!(prev.is_none()); |
141 | crate_id | 148 | crate_id |
142 | } | 149 | } |
143 | 150 | ||
151 | pub fn cfg_options(&self, crate_id: CrateId) -> &CfgOptions { | ||
152 | &self.arena[&crate_id].cfg_options | ||
153 | } | ||
154 | |||
144 | pub fn add_dep( | 155 | pub fn add_dep( |
145 | &mut self, | 156 | &mut self, |
146 | from: CrateId, | 157 | from: CrateId, |
@@ -221,14 +232,14 @@ impl CrateGraph { | |||
221 | 232 | ||
222 | #[cfg(test)] | 233 | #[cfg(test)] |
223 | mod tests { | 234 | mod tests { |
224 | use super::{CrateGraph, Edition::Edition2018, FileId, SmolStr}; | 235 | use super::{CfgOptions, CrateGraph, Edition::Edition2018, FileId, SmolStr}; |
225 | 236 | ||
226 | #[test] | 237 | #[test] |
227 | fn it_should_panic_because_of_cycle_dependencies() { | 238 | fn it_should_panic_because_of_cycle_dependencies() { |
228 | let mut graph = CrateGraph::default(); | 239 | let mut graph = CrateGraph::default(); |
229 | let crate1 = graph.add_crate_root(FileId(1u32), Edition2018); | 240 | let crate1 = graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default()); |
230 | let crate2 = graph.add_crate_root(FileId(2u32), Edition2018); | 241 | let crate2 = graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default()); |
231 | let crate3 = graph.add_crate_root(FileId(3u32), Edition2018); | 242 | let crate3 = graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default()); |
232 | assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok()); | 243 | assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok()); |
233 | assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok()); | 244 | assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok()); |
234 | assert!(graph.add_dep(crate3, SmolStr::new("crate1"), crate1).is_err()); | 245 | assert!(graph.add_dep(crate3, SmolStr::new("crate1"), crate1).is_err()); |
@@ -237,9 +248,9 @@ mod tests { | |||
237 | #[test] | 248 | #[test] |
238 | fn it_works() { | 249 | fn it_works() { |
239 | let mut graph = CrateGraph::default(); | 250 | let mut graph = CrateGraph::default(); |
240 | let crate1 = graph.add_crate_root(FileId(1u32), Edition2018); | 251 | let crate1 = graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default()); |
241 | let crate2 = graph.add_crate_root(FileId(2u32), Edition2018); | 252 | let crate2 = graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default()); |
242 | let crate3 = graph.add_crate_root(FileId(3u32), Edition2018); | 253 | let crate3 = graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default()); |
243 | assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok()); | 254 | assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok()); |
244 | assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok()); | 255 | assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok()); |
245 | } | 256 | } |
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml index d9bed4dda..cc117f84d 100644 --- a/crates/ra_hir/Cargo.toml +++ b/crates/ra_hir/Cargo.toml | |||
@@ -15,6 +15,7 @@ once_cell = "1.0.1" | |||
15 | 15 | ||
16 | ra_syntax = { path = "../ra_syntax" } | 16 | ra_syntax = { path = "../ra_syntax" } |
17 | ra_arena = { path = "../ra_arena" } | 17 | ra_arena = { path = "../ra_arena" } |
18 | ra_cfg = { path = "../ra_cfg" } | ||
18 | ra_db = { path = "../ra_db" } | 19 | ra_db = { path = "../ra_db" } |
19 | mbe = { path = "../ra_mbe", package = "ra_mbe" } | 20 | mbe = { path = "../ra_mbe", package = "ra_mbe" } |
20 | tt = { path = "../ra_tt", package = "ra_tt" } | 21 | tt = { path = "../ra_tt", package = "ra_tt" } |
diff --git a/crates/ra_hir/src/attr.rs b/crates/ra_hir/src/attr.rs new file mode 100644 index 000000000..f67e80bfd --- /dev/null +++ b/crates/ra_hir/src/attr.rs | |||
@@ -0,0 +1,80 @@ | |||
1 | //! A higher level attributes based on TokenTree, with also some shortcuts. | ||
2 | |||
3 | use std::sync::Arc; | ||
4 | |||
5 | use mbe::ast_to_token_tree; | ||
6 | use ra_cfg::CfgOptions; | ||
7 | use ra_syntax::{ | ||
8 | ast::{self, AstNode, AttrsOwner}, | ||
9 | SmolStr, | ||
10 | }; | ||
11 | use tt::Subtree; | ||
12 | |||
13 | use crate::{db::AstDatabase, path::Path, HirFileId, Source}; | ||
14 | |||
15 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
16 | pub(crate) struct Attr { | ||
17 | pub(crate) path: Path, | ||
18 | pub(crate) input: Option<AttrInput>, | ||
19 | } | ||
20 | |||
21 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
22 | pub enum AttrInput { | ||
23 | Literal(SmolStr), | ||
24 | TokenTree(Subtree), | ||
25 | } | ||
26 | |||
27 | impl Attr { | ||
28 | pub(crate) fn from_src( | ||
29 | Source { file_id, ast }: Source<ast::Attr>, | ||
30 | db: &impl AstDatabase, | ||
31 | ) -> Option<Attr> { | ||
32 | let path = Path::from_src(Source { file_id, ast: ast.path()? }, db)?; | ||
33 | let input = match ast.input() { | ||
34 | None => None, | ||
35 | Some(ast::AttrInput::Literal(lit)) => { | ||
36 | // FIXME: escape? raw string? | ||
37 | let value = lit.syntax().first_token()?.text().trim_matches('"').into(); | ||
38 | Some(AttrInput::Literal(value)) | ||
39 | } | ||
40 | Some(ast::AttrInput::TokenTree(tt)) => { | ||
41 | Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0)) | ||
42 | } | ||
43 | }; | ||
44 | |||
45 | Some(Attr { path, input }) | ||
46 | } | ||
47 | |||
48 | pub(crate) fn from_attrs_owner( | ||
49 | file_id: HirFileId, | ||
50 | owner: &dyn AttrsOwner, | ||
51 | db: &impl AstDatabase, | ||
52 | ) -> Option<Arc<[Attr]>> { | ||
53 | let mut attrs = owner.attrs().peekable(); | ||
54 | if attrs.peek().is_none() { | ||
55 | // Avoid heap allocation | ||
56 | return None; | ||
57 | } | ||
58 | Some(attrs.flat_map(|ast| Attr::from_src(Source { file_id, ast }, db)).collect()) | ||
59 | } | ||
60 | |||
61 | pub(crate) fn is_simple_atom(&self, name: &str) -> bool { | ||
62 | // FIXME: Avoid cloning | ||
63 | self.path.as_ident().map_or(false, |s| s.to_string() == name) | ||
64 | } | ||
65 | |||
66 | pub(crate) fn as_cfg(&self) -> Option<&Subtree> { | ||
67 | if self.is_simple_atom("cfg") { | ||
68 | match &self.input { | ||
69 | Some(AttrInput::TokenTree(subtree)) => Some(subtree), | ||
70 | _ => None, | ||
71 | } | ||
72 | } else { | ||
73 | None | ||
74 | } | ||
75 | } | ||
76 | |||
77 | pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option<bool> { | ||
78 | cfg_options.is_cfg_enabled(self.as_cfg()?) | ||
79 | } | ||
80 | } | ||
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 8cf74ddc7..55dfc393b 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs | |||
@@ -4,12 +4,14 @@ use rustc_hash::FxHashMap; | |||
4 | use std::sync::Arc; | 4 | use std::sync::Arc; |
5 | 5 | ||
6 | use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; | 6 | use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; |
7 | use ra_cfg::CfgOptions; | ||
7 | use ra_syntax::{ | 8 | use ra_syntax::{ |
8 | ast::{self, AstNode}, | 9 | ast::{self, AstNode}, |
9 | AstPtr, | 10 | AstPtr, |
10 | }; | 11 | }; |
11 | 12 | ||
12 | use crate::{ | 13 | use crate::{ |
14 | attr::Attr, | ||
13 | code_model::{Module, ModuleSource}, | 15 | code_model::{Module, ModuleSource}, |
14 | db::{AstDatabase, DefDatabase, HirDatabase}, | 16 | db::{AstDatabase, DefDatabase, HirDatabase}, |
15 | generics::HasGenericParams, | 17 | generics::HasGenericParams, |
@@ -176,6 +178,7 @@ pub struct ModuleImplBlocks { | |||
176 | impl ModuleImplBlocks { | 178 | impl ModuleImplBlocks { |
177 | fn collect( | 179 | fn collect( |
178 | db: &(impl DefDatabase + AstDatabase), | 180 | db: &(impl DefDatabase + AstDatabase), |
181 | cfg_options: &CfgOptions, | ||
179 | module: Module, | 182 | module: Module, |
180 | source_map: &mut ImplSourceMap, | 183 | source_map: &mut ImplSourceMap, |
181 | ) -> Self { | 184 | ) -> Self { |
@@ -188,11 +191,11 @@ impl ModuleImplBlocks { | |||
188 | let src = m.module.definition_source(db); | 191 | let src = m.module.definition_source(db); |
189 | match &src.ast { | 192 | match &src.ast { |
190 | ModuleSource::SourceFile(node) => { | 193 | ModuleSource::SourceFile(node) => { |
191 | m.collect_from_item_owner(db, source_map, node, src.file_id) | 194 | m.collect_from_item_owner(db, cfg_options, source_map, node, src.file_id) |
192 | } | 195 | } |
193 | ModuleSource::Module(node) => { | 196 | ModuleSource::Module(node) => { |
194 | let item_list = node.item_list().expect("inline module should have item list"); | 197 | let item_list = node.item_list().expect("inline module should have item list"); |
195 | m.collect_from_item_owner(db, source_map, &item_list, src.file_id) | 198 | m.collect_from_item_owner(db, cfg_options, source_map, &item_list, src.file_id) |
196 | } | 199 | } |
197 | }; | 200 | }; |
198 | m | 201 | m |
@@ -201,6 +204,7 @@ impl ModuleImplBlocks { | |||
201 | fn collect_from_item_owner( | 204 | fn collect_from_item_owner( |
202 | &mut self, | 205 | &mut self, |
203 | db: &(impl DefDatabase + AstDatabase), | 206 | db: &(impl DefDatabase + AstDatabase), |
207 | cfg_options: &CfgOptions, | ||
204 | source_map: &mut ImplSourceMap, | 208 | source_map: &mut ImplSourceMap, |
205 | owner: &dyn ast::ModuleItemOwner, | 209 | owner: &dyn ast::ModuleItemOwner, |
206 | file_id: HirFileId, | 210 | file_id: HirFileId, |
@@ -208,6 +212,13 @@ impl ModuleImplBlocks { | |||
208 | for item in owner.items_with_macros() { | 212 | for item in owner.items_with_macros() { |
209 | match item { | 213 | match item { |
210 | ast::ItemOrMacro::Item(ast::ModuleItem::ImplBlock(impl_block_ast)) => { | 214 | ast::ItemOrMacro::Item(ast::ModuleItem::ImplBlock(impl_block_ast)) => { |
215 | let attrs = Attr::from_attrs_owner(file_id, &impl_block_ast, db); | ||
216 | if attrs.map_or(false, |attrs| { | ||
217 | attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false)) | ||
218 | }) { | ||
219 | continue; | ||
220 | } | ||
221 | |||
211 | let impl_block = ImplData::from_ast(db, file_id, self.module, &impl_block_ast); | 222 | let impl_block = ImplData::from_ast(db, file_id, self.module, &impl_block_ast); |
212 | let id = self.impls.alloc(impl_block); | 223 | let id = self.impls.alloc(impl_block); |
213 | for &impl_item in &self.impls[id].items { | 224 | for &impl_item in &self.impls[id].items { |
@@ -218,6 +229,13 @@ impl ModuleImplBlocks { | |||
218 | } | 229 | } |
219 | ast::ItemOrMacro::Item(_) => (), | 230 | ast::ItemOrMacro::Item(_) => (), |
220 | ast::ItemOrMacro::Macro(macro_call) => { | 231 | ast::ItemOrMacro::Macro(macro_call) => { |
232 | let attrs = Attr::from_attrs_owner(file_id, ¯o_call, db); | ||
233 | if attrs.map_or(false, |attrs| { | ||
234 | attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false)) | ||
235 | }) { | ||
236 | continue; | ||
237 | } | ||
238 | |||
221 | //FIXME: we should really cut down on the boilerplate required to process a macro | 239 | //FIXME: we should really cut down on the boilerplate required to process a macro |
222 | let ast_id = db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id); | 240 | let ast_id = db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id); |
223 | if let Some(path) = macro_call | 241 | if let Some(path) = macro_call |
@@ -231,7 +249,13 @@ impl ModuleImplBlocks { | |||
231 | if let Some(item_list) = | 249 | if let Some(item_list) = |
232 | db.parse_or_expand(file_id).and_then(ast::MacroItems::cast) | 250 | db.parse_or_expand(file_id).and_then(ast::MacroItems::cast) |
233 | { | 251 | { |
234 | self.collect_from_item_owner(db, source_map, &item_list, file_id) | 252 | self.collect_from_item_owner( |
253 | db, | ||
254 | cfg_options, | ||
255 | source_map, | ||
256 | &item_list, | ||
257 | file_id, | ||
258 | ) | ||
235 | } | 259 | } |
236 | } | 260 | } |
237 | } | 261 | } |
@@ -246,8 +270,10 @@ pub(crate) fn impls_in_module_with_source_map_query( | |||
246 | module: Module, | 270 | module: Module, |
247 | ) -> (Arc<ModuleImplBlocks>, Arc<ImplSourceMap>) { | 271 | ) -> (Arc<ModuleImplBlocks>, Arc<ImplSourceMap>) { |
248 | let mut source_map = ImplSourceMap::default(); | 272 | let mut source_map = ImplSourceMap::default(); |
273 | let crate_graph = db.crate_graph(); | ||
274 | let cfg_options = crate_graph.cfg_options(module.krate.crate_id()); | ||
249 | 275 | ||
250 | let result = ModuleImplBlocks::collect(db, module, &mut source_map); | 276 | let result = ModuleImplBlocks::collect(db, cfg_options, module, &mut source_map); |
251 | (Arc::new(result), Arc::new(source_map)) | 277 | (Arc::new(result), Arc::new(source_map)) |
252 | } | 278 | } |
253 | 279 | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 00031deba..4340e9d34 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -44,6 +44,7 @@ mod traits; | |||
44 | mod type_alias; | 44 | mod type_alias; |
45 | mod type_ref; | 45 | mod type_ref; |
46 | mod ty; | 46 | mod ty; |
47 | mod attr; | ||
47 | mod impl_block; | 48 | mod impl_block; |
48 | mod expr; | 49 | mod expr; |
49 | mod lang_item; | 50 | mod lang_item; |
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 50feb98fb..f750986b8 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs | |||
@@ -3,6 +3,7 @@ | |||
3 | use std::{panic, sync::Arc}; | 3 | use std::{panic, sync::Arc}; |
4 | 4 | ||
5 | use parking_lot::Mutex; | 5 | use parking_lot::Mutex; |
6 | use ra_cfg::CfgOptions; | ||
6 | use ra_db::{ | 7 | use ra_db::{ |
7 | salsa, CrateGraph, CrateId, Edition, FileId, FilePosition, SourceDatabase, SourceRoot, | 8 | salsa, CrateGraph, CrateId, Edition, FileId, FilePosition, SourceDatabase, SourceRoot, |
8 | SourceRootId, | 9 | SourceRootId, |
@@ -74,13 +75,13 @@ impl MockDatabase { | |||
74 | pub fn set_crate_graph_from_fixture(&mut self, graph: CrateGraphFixture) { | 75 | pub fn set_crate_graph_from_fixture(&mut self, graph: CrateGraphFixture) { |
75 | let mut ids = FxHashMap::default(); | 76 | let mut ids = FxHashMap::default(); |
76 | let mut crate_graph = CrateGraph::default(); | 77 | let mut crate_graph = CrateGraph::default(); |
77 | for (crate_name, (crate_root, edition, _)) in graph.0.iter() { | 78 | for (crate_name, (crate_root, edition, cfg_options, _)) in graph.0.iter() { |
78 | let crate_root = self.file_id_of(&crate_root); | 79 | let crate_root = self.file_id_of(&crate_root); |
79 | let crate_id = crate_graph.add_crate_root(crate_root, *edition); | 80 | let crate_id = crate_graph.add_crate_root(crate_root, *edition, cfg_options.clone()); |
80 | Arc::make_mut(&mut self.crate_names).insert(crate_id, crate_name.clone()); | 81 | Arc::make_mut(&mut self.crate_names).insert(crate_id, crate_name.clone()); |
81 | ids.insert(crate_name, crate_id); | 82 | ids.insert(crate_name, crate_id); |
82 | } | 83 | } |
83 | for (crate_name, (_, _, deps)) in graph.0.iter() { | 84 | for (crate_name, (_, _, _, deps)) in graph.0.iter() { |
84 | let from = ids[crate_name]; | 85 | let from = ids[crate_name]; |
85 | for dep in deps { | 86 | for dep in deps { |
86 | let to = ids[dep]; | 87 | let to = ids[dep]; |
@@ -184,7 +185,7 @@ impl MockDatabase { | |||
184 | 185 | ||
185 | if is_crate_root { | 186 | if is_crate_root { |
186 | let mut crate_graph = CrateGraph::default(); | 187 | let mut crate_graph = CrateGraph::default(); |
187 | crate_graph.add_crate_root(file_id, Edition::Edition2018); | 188 | crate_graph.add_crate_root(file_id, Edition::Edition2018, CfgOptions::default()); |
188 | self.set_crate_graph(Arc::new(crate_graph)); | 189 | self.set_crate_graph(Arc::new(crate_graph)); |
189 | } | 190 | } |
190 | file_id | 191 | file_id |
@@ -268,19 +269,27 @@ impl MockDatabase { | |||
268 | } | 269 | } |
269 | 270 | ||
270 | #[derive(Default)] | 271 | #[derive(Default)] |
271 | pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, Vec<String>))>); | 272 | pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, CfgOptions, Vec<String>))>); |
272 | 273 | ||
273 | #[macro_export] | 274 | #[macro_export] |
274 | macro_rules! crate_graph { | 275 | macro_rules! crate_graph { |
275 | ($($crate_name:literal: ($crate_path:literal, $($edition:literal,)? [$($dep:literal),*]),)*) => {{ | 276 | ($( |
277 | $crate_name:literal: ( | ||
278 | $crate_path:literal, | ||
279 | $($edition:literal,)? | ||
280 | [$($dep:literal),*] | ||
281 | $(,$cfg:expr)? | ||
282 | ), | ||
283 | )*) => {{ | ||
276 | let mut res = $crate::mock::CrateGraphFixture::default(); | 284 | let mut res = $crate::mock::CrateGraphFixture::default(); |
277 | $( | 285 | $( |
278 | #[allow(unused_mut, unused_assignments)] | 286 | #[allow(unused_mut, unused_assignments)] |
279 | let mut edition = ra_db::Edition::Edition2018; | 287 | let mut edition = ra_db::Edition::Edition2018; |
280 | $(edition = ra_db::Edition::from_string($edition);)? | 288 | $(edition = ra_db::Edition::from_string($edition);)? |
289 | let cfg_options = { ::ra_cfg::CfgOptions::default() $(; $cfg)? }; | ||
281 | res.0.push(( | 290 | res.0.push(( |
282 | $crate_name.to_string(), | 291 | $crate_name.to_string(), |
283 | ($crate_path.to_string(), edition, vec![$($dep.to_string()),*]) | 292 | ($crate_path.to_string(), edition, cfg_options, vec![$($dep.to_string()),*]) |
284 | )); | 293 | )); |
285 | )* | 294 | )* |
286 | res | 295 | res |
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index a568fdabd..cef2dc9d2 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_cfg::CfgOptions; | ||
3 | use ra_db::FileId; | 4 | use ra_db::FileId; |
4 | use ra_syntax::{ast, SmolStr}; | 5 | use ra_syntax::{ast, SmolStr}; |
5 | use rustc_hash::FxHashMap; | 6 | use rustc_hash::FxHashMap; |
@@ -35,6 +36,9 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C | |||
35 | } | 36 | } |
36 | } | 37 | } |
37 | 38 | ||
39 | let crate_graph = db.crate_graph(); | ||
40 | let cfg_options = crate_graph.cfg_options(def_map.krate().crate_id()); | ||
41 | |||
38 | let mut collector = DefCollector { | 42 | let mut collector = DefCollector { |
39 | db, | 43 | db, |
40 | def_map, | 44 | def_map, |
@@ -42,6 +46,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C | |||
42 | unresolved_imports: Vec::new(), | 46 | unresolved_imports: Vec::new(), |
43 | unexpanded_macros: Vec::new(), | 47 | unexpanded_macros: Vec::new(), |
44 | macro_stack_monitor: MacroStackMonitor::default(), | 48 | macro_stack_monitor: MacroStackMonitor::default(), |
49 | cfg_options, | ||
45 | }; | 50 | }; |
46 | collector.collect(); | 51 | collector.collect(); |
47 | collector.finish() | 52 | collector.finish() |
@@ -76,8 +81,8 @@ impl MacroStackMonitor { | |||
76 | } | 81 | } |
77 | 82 | ||
78 | /// Walks the tree of module recursively | 83 | /// Walks the tree of module recursively |
79 | struct DefCollector<DB> { | 84 | struct DefCollector<'a, DB> { |
80 | db: DB, | 85 | db: &'a DB, |
81 | def_map: CrateDefMap, | 86 | def_map: CrateDefMap, |
82 | glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>, | 87 | glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>, |
83 | unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, | 88 | unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, |
@@ -86,9 +91,11 @@ struct DefCollector<DB> { | |||
86 | /// Some macro use `$tt:tt which mean we have to handle the macro perfectly | 91 | /// Some macro use `$tt:tt which mean we have to handle the macro perfectly |
87 | /// To prevent stack overflow, we add a deep counter here for prevent that. | 92 | /// To prevent stack overflow, we add a deep counter here for prevent that. |
88 | macro_stack_monitor: MacroStackMonitor, | 93 | macro_stack_monitor: MacroStackMonitor, |
94 | |||
95 | cfg_options: &'a CfgOptions, | ||
89 | } | 96 | } |
90 | 97 | ||
91 | impl<'a, DB> DefCollector<&'a DB> | 98 | impl<DB> DefCollector<'_, DB> |
92 | where | 99 | where |
93 | DB: DefDatabase, | 100 | DB: DefDatabase, |
94 | { | 101 | { |
@@ -506,7 +513,7 @@ struct ModCollector<'a, D> { | |||
506 | parent_module: Option<ParentModule<'a>>, | 513 | parent_module: Option<ParentModule<'a>>, |
507 | } | 514 | } |
508 | 515 | ||
509 | impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> | 516 | impl<DB> ModCollector<'_, &'_ mut DefCollector<'_, DB>> |
510 | where | 517 | where |
511 | DB: DefDatabase, | 518 | DB: DefDatabase, |
512 | { | 519 | { |
@@ -523,24 +530,27 @@ where | |||
523 | // `#[macro_use] extern crate` is hoisted to imports macros before collecting | 530 | // `#[macro_use] extern crate` is hoisted to imports macros before collecting |
524 | // any other items. | 531 | // any other items. |
525 | for item in items { | 532 | for item in items { |
526 | if let raw::RawItem::Import(import_id) = *item { | 533 | if self.is_cfg_enabled(&item.attrs) { |
527 | let import = self.raw_items[import_id].clone(); | 534 | if let raw::RawItemKind::Import(import_id) = item.kind { |
528 | if import.is_extern_crate && import.is_macro_use { | 535 | let import = self.raw_items[import_id].clone(); |
529 | self.def_collector.import_macros_from_extern_crate(self.module_id, &import); | 536 | if import.is_extern_crate && import.is_macro_use { |
537 | self.def_collector.import_macros_from_extern_crate(self.module_id, &import); | ||
538 | } | ||
530 | } | 539 | } |
531 | } | 540 | } |
532 | } | 541 | } |
533 | 542 | ||
534 | for item in items { | 543 | for item in items { |
535 | match *item { | 544 | if self.is_cfg_enabled(&item.attrs) { |
536 | raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), | 545 | match item.kind { |
537 | raw::RawItem::Import(import_id) => self.def_collector.unresolved_imports.push(( | 546 | raw::RawItemKind::Module(m) => self.collect_module(&self.raw_items[m]), |
538 | self.module_id, | 547 | raw::RawItemKind::Import(import_id) => self |
539 | import_id, | 548 | .def_collector |
540 | self.raw_items[import_id].clone(), | 549 | .unresolved_imports |
541 | )), | 550 | .push((self.module_id, import_id, self.raw_items[import_id].clone())), |
542 | raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]), | 551 | raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]), |
543 | raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]), | 552 | raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]), |
553 | } | ||
544 | } | 554 | } |
545 | } | 555 | } |
546 | } | 556 | } |
@@ -703,6 +713,14 @@ where | |||
703 | self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_); | 713 | self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_); |
704 | } | 714 | } |
705 | } | 715 | } |
716 | |||
717 | fn is_cfg_enabled(&self, attrs: &raw::Attrs) -> bool { | ||
718 | attrs.as_ref().map_or(true, |attrs| { | ||
719 | attrs | ||
720 | .iter() | ||
721 | .all(|attr| attr.is_cfg_enabled(&self.def_collector.cfg_options) != Some(false)) | ||
722 | }) | ||
723 | } | ||
706 | } | 724 | } |
707 | 725 | ||
708 | fn is_macro_rules(path: &Path) -> bool { | 726 | fn is_macro_rules(path: &Path) -> bool { |
@@ -730,6 +748,7 @@ mod tests { | |||
730 | unresolved_imports: Vec::new(), | 748 | unresolved_imports: Vec::new(), |
731 | unexpanded_macros: Vec::new(), | 749 | unexpanded_macros: Vec::new(), |
732 | macro_stack_monitor: monitor, | 750 | macro_stack_monitor: monitor, |
751 | cfg_options: &CfgOptions::default(), | ||
733 | }; | 752 | }; |
734 | collector.collect(); | 753 | collector.collect(); |
735 | collector.finish() | 754 | collector.finish() |
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 606bd1a95..623b343c4 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs | |||
@@ -10,6 +10,7 @@ use ra_syntax::{ | |||
10 | use test_utils::tested_by; | 10 | use test_utils::tested_by; |
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
13 | attr::Attr, | ||
13 | db::{AstDatabase, DefDatabase}, | 14 | db::{AstDatabase, DefDatabase}, |
14 | AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source, | 15 | AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source, |
15 | }; | 16 | }; |
@@ -119,8 +120,17 @@ impl Index<Macro> for RawItems { | |||
119 | } | 120 | } |
120 | } | 121 | } |
121 | 122 | ||
123 | // Avoid heap allocation on items without attributes. | ||
124 | pub(super) type Attrs = Option<Arc<[Attr]>>; | ||
125 | |||
126 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
127 | pub(super) struct RawItem { | ||
128 | pub(super) attrs: Attrs, | ||
129 | pub(super) kind: RawItemKind, | ||
130 | } | ||
131 | |||
122 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 132 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
123 | pub(super) enum RawItem { | 133 | pub(super) enum RawItemKind { |
124 | Module(Module), | 134 | Module(Module), |
125 | Import(ImportId), | 135 | Import(ImportId), |
126 | Def(Def), | 136 | Def(Def), |
@@ -215,6 +225,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
215 | } | 225 | } |
216 | 226 | ||
217 | fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) { | 227 | fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) { |
228 | let attrs = self.parse_attrs(&item); | ||
218 | let (kind, name) = match item { | 229 | let (kind, name) = match item { |
219 | ast::ModuleItem::Module(module) => { | 230 | ast::ModuleItem::Module(module) => { |
220 | self.add_module(current_module, module); | 231 | self.add_module(current_module, module); |
@@ -263,7 +274,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
263 | if let Some(name) = name { | 274 | if let Some(name) = name { |
264 | let name = name.as_name(); | 275 | let name = name.as_name(); |
265 | let def = self.raw_items.defs.alloc(DefData { name, kind }); | 276 | let def = self.raw_items.defs.alloc(DefData { name, kind }); |
266 | self.push_item(current_module, RawItem::Def(def)) | 277 | self.push_item(current_module, attrs, RawItemKind::Def(def)); |
267 | } | 278 | } |
268 | } | 279 | } |
269 | 280 | ||
@@ -272,8 +283,10 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
272 | Some(it) => it.as_name(), | 283 | Some(it) => it.as_name(), |
273 | None => return, | 284 | None => return, |
274 | }; | 285 | }; |
286 | let attrs = self.parse_attrs(&module); | ||
275 | 287 | ||
276 | let ast_id = self.source_ast_id_map.ast_id(&module); | 288 | let ast_id = self.source_ast_id_map.ast_id(&module); |
289 | // FIXME: cfg_attr | ||
277 | let is_macro_use = module.has_atom_attr("macro_use"); | 290 | let is_macro_use = module.has_atom_attr("macro_use"); |
278 | if module.has_semi() { | 291 | if module.has_semi() { |
279 | let attr_path = extract_mod_path_attribute(&module); | 292 | let attr_path = extract_mod_path_attribute(&module); |
@@ -283,7 +296,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
283 | attr_path, | 296 | attr_path, |
284 | is_macro_use, | 297 | is_macro_use, |
285 | }); | 298 | }); |
286 | self.push_item(current_module, RawItem::Module(item)); | 299 | self.push_item(current_module, attrs, RawItemKind::Module(item)); |
287 | return; | 300 | return; |
288 | } | 301 | } |
289 | 302 | ||
@@ -297,14 +310,16 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
297 | is_macro_use, | 310 | is_macro_use, |
298 | }); | 311 | }); |
299 | self.process_module(Some(item), item_list); | 312 | self.process_module(Some(item), item_list); |
300 | self.push_item(current_module, RawItem::Module(item)); | 313 | self.push_item(current_module, attrs, RawItemKind::Module(item)); |
301 | return; | 314 | return; |
302 | } | 315 | } |
303 | tested_by!(name_res_works_for_broken_modules); | 316 | tested_by!(name_res_works_for_broken_modules); |
304 | } | 317 | } |
305 | 318 | ||
306 | fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) { | 319 | fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) { |
320 | // FIXME: cfg_attr | ||
307 | let is_prelude = use_item.has_atom_attr("prelude_import"); | 321 | let is_prelude = use_item.has_atom_attr("prelude_import"); |
322 | let attrs = self.parse_attrs(&use_item); | ||
308 | 323 | ||
309 | Path::expand_use_item( | 324 | Path::expand_use_item( |
310 | Source { ast: use_item, file_id: self.file_id }, | 325 | Source { ast: use_item, file_id: self.file_id }, |
@@ -318,7 +333,12 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
318 | is_extern_crate: false, | 333 | is_extern_crate: false, |
319 | is_macro_use: false, | 334 | is_macro_use: false, |
320 | }; | 335 | }; |
321 | self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree))); | 336 | self.push_import( |
337 | current_module, | ||
338 | attrs.clone(), | ||
339 | import_data, | ||
340 | Either::A(AstPtr::new(use_tree)), | ||
341 | ); | ||
322 | }, | 342 | }, |
323 | ) | 343 | ) |
324 | } | 344 | } |
@@ -331,6 +351,8 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
331 | if let Some(name_ref) = extern_crate.name_ref() { | 351 | if let Some(name_ref) = extern_crate.name_ref() { |
332 | let path = Path::from_name_ref(&name_ref); | 352 | let path = Path::from_name_ref(&name_ref); |
333 | let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); | 353 | let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); |
354 | let attrs = self.parse_attrs(&extern_crate); | ||
355 | // FIXME: cfg_attr | ||
334 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); | 356 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); |
335 | let import_data = ImportData { | 357 | let import_data = ImportData { |
336 | path, | 358 | path, |
@@ -340,11 +362,17 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
340 | is_extern_crate: true, | 362 | is_extern_crate: true, |
341 | is_macro_use, | 363 | is_macro_use, |
342 | }; | 364 | }; |
343 | self.push_import(current_module, import_data, Either::B(AstPtr::new(&extern_crate))); | 365 | self.push_import( |
366 | current_module, | ||
367 | attrs, | ||
368 | import_data, | ||
369 | Either::B(AstPtr::new(&extern_crate)), | ||
370 | ); | ||
344 | } | 371 | } |
345 | } | 372 | } |
346 | 373 | ||
347 | fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) { | 374 | fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) { |
375 | let attrs = self.parse_attrs(&m); | ||
348 | let path = match m | 376 | let path = match m |
349 | .path() | 377 | .path() |
350 | .and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db)) | 378 | .and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db)) |
@@ -355,24 +383,26 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
355 | 383 | ||
356 | let name = m.name().map(|it| it.as_name()); | 384 | let name = m.name().map(|it| it.as_name()); |
357 | let ast_id = self.source_ast_id_map.ast_id(&m); | 385 | let ast_id = self.source_ast_id_map.ast_id(&m); |
386 | // FIXME: cfg_attr | ||
358 | let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export"); | 387 | let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export"); |
359 | 388 | ||
360 | let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); | 389 | let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); |
361 | self.push_item(current_module, RawItem::Macro(m)); | 390 | self.push_item(current_module, attrs, RawItemKind::Macro(m)); |
362 | } | 391 | } |
363 | 392 | ||
364 | fn push_import( | 393 | fn push_import( |
365 | &mut self, | 394 | &mut self, |
366 | current_module: Option<Module>, | 395 | current_module: Option<Module>, |
396 | attrs: Attrs, | ||
367 | data: ImportData, | 397 | data: ImportData, |
368 | source: ImportSourcePtr, | 398 | source: ImportSourcePtr, |
369 | ) { | 399 | ) { |
370 | let import = self.raw_items.imports.alloc(data); | 400 | let import = self.raw_items.imports.alloc(data); |
371 | self.source_map.insert(import, source); | 401 | self.source_map.insert(import, source); |
372 | self.push_item(current_module, RawItem::Import(import)) | 402 | self.push_item(current_module, attrs, RawItemKind::Import(import)) |
373 | } | 403 | } |
374 | 404 | ||
375 | fn push_item(&mut self, current_module: Option<Module>, item: RawItem) { | 405 | fn push_item(&mut self, current_module: Option<Module>, attrs: Attrs, kind: RawItemKind) { |
376 | match current_module { | 406 | match current_module { |
377 | Some(module) => match &mut self.raw_items.modules[module] { | 407 | Some(module) => match &mut self.raw_items.modules[module] { |
378 | ModuleData::Definition { items, .. } => items, | 408 | ModuleData::Definition { items, .. } => items, |
@@ -380,7 +410,11 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
380 | }, | 410 | }, |
381 | None => &mut self.raw_items.items, | 411 | None => &mut self.raw_items.items, |
382 | } | 412 | } |
383 | .push(item) | 413 | .push(RawItem { attrs, kind }) |
414 | } | ||
415 | |||
416 | fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs { | ||
417 | Attr::from_attrs_owner(self.file_id, item, self.db) | ||
384 | } | 418 | } |
385 | } | 419 | } |
386 | 420 | ||
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index bc4b47b70..34dd79574 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs | |||
@@ -7,6 +7,7 @@ mod mod_resolution; | |||
7 | use std::sync::Arc; | 7 | use std::sync::Arc; |
8 | 8 | ||
9 | use insta::assert_snapshot; | 9 | use insta::assert_snapshot; |
10 | use ra_cfg::CfgOptions; | ||
10 | use ra_db::SourceDatabase; | 11 | use ra_db::SourceDatabase; |
11 | use test_utils::covers; | 12 | use test_utils::covers; |
12 | 13 | ||
@@ -507,3 +508,72 @@ fn values_dont_shadow_extern_crates() { | |||
507 | â‹®foo: v | 508 | â‹®foo: v |
508 | "###); | 509 | "###); |
509 | } | 510 | } |
511 | |||
512 | #[test] | ||
513 | fn cfg_not_test() { | ||
514 | let map = def_map_with_crate_graph( | ||
515 | r#" | ||
516 | //- /main.rs | ||
517 | use {Foo, Bar, Baz}; | ||
518 | //- /lib.rs | ||
519 | #[prelude_import] | ||
520 | pub use self::prelude::*; | ||
521 | mod prelude { | ||
522 | #[cfg(test)] | ||
523 | pub struct Foo; | ||
524 | #[cfg(not(test))] | ||
525 | pub struct Bar; | ||
526 | #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] | ||
527 | pub struct Baz; | ||
528 | } | ||
529 | "#, | ||
530 | crate_graph! { | ||
531 | "main": ("/main.rs", ["std"]), | ||
532 | "std": ("/lib.rs", []), | ||
533 | }, | ||
534 | ); | ||
535 | |||
536 | assert_snapshot!(map, @r###" | ||
537 | â‹®crate | ||
538 | â‹®Bar: t v | ||
539 | â‹®Baz: _ | ||
540 | â‹®Foo: _ | ||
541 | "###); | ||
542 | } | ||
543 | |||
544 | #[test] | ||
545 | fn cfg_test() { | ||
546 | let map = def_map_with_crate_graph( | ||
547 | r#" | ||
548 | //- /main.rs | ||
549 | use {Foo, Bar, Baz}; | ||
550 | //- /lib.rs | ||
551 | #[prelude_import] | ||
552 | pub use self::prelude::*; | ||
553 | mod prelude { | ||
554 | #[cfg(test)] | ||
555 | pub struct Foo; | ||
556 | #[cfg(not(test))] | ||
557 | pub struct Bar; | ||
558 | #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] | ||
559 | pub struct Baz; | ||
560 | } | ||
561 | "#, | ||
562 | crate_graph! { | ||
563 | "main": ("/main.rs", ["std"]), | ||
564 | "std": ("/lib.rs", [], CfgOptions::default() | ||
565 | .atom("test".into()) | ||
566 | .key_value("feature".into(), "foo".into()) | ||
567 | .key_value("feature".into(), "bar".into()) | ||
568 | .key_value("opt".into(), "42".into()) | ||
569 | ), | ||
570 | }, | ||
571 | ); | ||
572 | |||
573 | assert_snapshot!(map, @r###" | ||
574 | â‹®crate | ||
575 | â‹®Bar: _ | ||
576 | â‹®Baz: t v | ||
577 | â‹®Foo: t v | ||
578 | "###); | ||
579 | } | ||
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 66f63ca24..25dad81eb 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -3,6 +3,7 @@ use std::sync::Arc; | |||
3 | 3 | ||
4 | use insta::assert_snapshot; | 4 | use insta::assert_snapshot; |
5 | 5 | ||
6 | use ra_cfg::CfgOptions; | ||
6 | use ra_db::{salsa::Database, FilePosition, SourceDatabase}; | 7 | use ra_db::{salsa::Database, FilePosition, SourceDatabase}; |
7 | use ra_syntax::{ | 8 | use ra_syntax::{ |
8 | algo, | 9 | algo, |
@@ -24,6 +25,50 @@ mod never_type; | |||
24 | mod coercion; | 25 | mod coercion; |
25 | 26 | ||
26 | #[test] | 27 | #[test] |
28 | fn cfg_impl_block() { | ||
29 | let (mut db, pos) = MockDatabase::with_position( | ||
30 | r#" | ||
31 | //- /main.rs | ||
32 | use foo::S as T; | ||
33 | struct S; | ||
34 | |||
35 | #[cfg(test)] | ||
36 | impl S { | ||
37 | fn foo1(&self) -> i32 { 0 } | ||
38 | } | ||
39 | |||
40 | #[cfg(not(test))] | ||
41 | impl S { | ||
42 | fn foo2(&self) -> i32 { 0 } | ||
43 | } | ||
44 | |||
45 | fn test() { | ||
46 | let t = (S.foo1(), S.foo2(), T.foo3(), T.foo4()); | ||
47 | t<|>; | ||
48 | } | ||
49 | |||
50 | //- /foo.rs | ||
51 | struct S; | ||
52 | |||
53 | #[cfg(not(test))] | ||
54 | impl S { | ||
55 | fn foo3(&self) -> i32 { 0 } | ||
56 | } | ||
57 | |||
58 | #[cfg(test)] | ||
59 | impl S { | ||
60 | fn foo4(&self) -> i32 { 0 } | ||
61 | } | ||
62 | "#, | ||
63 | ); | ||
64 | db.set_crate_graph_from_fixture(crate_graph! { | ||
65 | "main": ("/main.rs", ["foo"], CfgOptions::default().atom("test".into())), | ||
66 | "foo": ("/foo.rs", []), | ||
67 | }); | ||
68 | assert_eq!("(i32, {unknown}, i32, {unknown})", type_at_pos(&db, pos)); | ||
69 | } | ||
70 | |||
71 | #[test] | ||
27 | fn infer_await() { | 72 | fn infer_await() { |
28 | let (mut db, pos) = MockDatabase::with_position( | 73 | let (mut db, pos) = MockDatabase::with_position( |
29 | r#" | 74 | r#" |
diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml index 6bbf9d5dd..f919a2d61 100644 --- a/crates/ra_ide_api/Cargo.toml +++ b/crates/ra_ide_api/Cargo.toml | |||
@@ -23,6 +23,7 @@ rand = { version = "0.7.0", features = ["small_rng"] } | |||
23 | ra_syntax = { path = "../ra_syntax" } | 23 | ra_syntax = { path = "../ra_syntax" } |
24 | ra_text_edit = { path = "../ra_text_edit" } | 24 | ra_text_edit = { path = "../ra_text_edit" } |
25 | ra_db = { path = "../ra_db" } | 25 | ra_db = { path = "../ra_db" } |
26 | ra_cfg = { path = "../ra_cfg" } | ||
26 | ra_fmt = { path = "../ra_fmt" } | 27 | ra_fmt = { path = "../ra_fmt" } |
27 | ra_prof = { path = "../ra_prof" } | 28 | ra_prof = { path = "../ra_prof" } |
28 | hir = { path = "../ra_hir", package = "ra_hir" } | 29 | hir = { path = "../ra_hir", package = "ra_hir" } |
diff --git a/crates/ra_ide_api/src/completion/complete_fn_param.rs b/crates/ra_ide_api/src/completion/complete_fn_param.rs index 844a63f6c..3e936e3ec 100644 --- a/crates/ra_ide_api/src/completion/complete_fn_param.rs +++ b/crates/ra_ide_api/src/completion/complete_fn_param.rs | |||
@@ -1,9 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ast, match_ast, AstNode}; |
4 | algo::visit::{visitor_ctx, VisitorCtx}, | ||
5 | ast, AstNode, | ||
6 | }; | ||
7 | use rustc_hash::FxHashMap; | 4 | use rustc_hash::FxHashMap; |
8 | 5 | ||
9 | use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; | 6 | use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; |
@@ -19,10 +16,13 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) | |||
19 | 16 | ||
20 | let mut params = FxHashMap::default(); | 17 | let mut params = FxHashMap::default(); |
21 | for node in ctx.token.parent().ancestors() { | 18 | for node in ctx.token.parent().ancestors() { |
22 | let _ = visitor_ctx(&mut params) | 19 | match_ast! { |
23 | .visit::<ast::SourceFile, _>(process) | 20 | match node { |
24 | .visit::<ast::ItemList, _>(process) | 21 | ast::SourceFile(it) => { process(it, &mut params) }, |
25 | .accept(&node); | 22 | ast::ItemList(it) => { process(it, &mut params) }, |
23 | _ => (), | ||
24 | } | ||
25 | } | ||
26 | } | 26 | } |
27 | params | 27 | params |
28 | .into_iter() | 28 | .into_iter() |
diff --git a/crates/ra_ide_api/src/completion/complete_keyword.rs b/crates/ra_ide_api/src/completion/complete_keyword.rs index 3f121d45c..48c688a08 100644 --- a/crates/ra_ide_api/src/completion/complete_keyword.rs +++ b/crates/ra_ide_api/src/completion/complete_keyword.rs | |||
@@ -1,9 +1,8 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::visit::{visitor, Visitor}, | ||
5 | ast::{self, LoopBodyOwner}, | 4 | ast::{self, LoopBodyOwner}, |
6 | AstNode, | 5 | match_ast, AstNode, |
7 | SyntaxKind::*, | 6 | SyntaxKind::*, |
8 | SyntaxToken, | 7 | SyntaxToken, |
9 | }; | 8 | }; |
@@ -84,12 +83,15 @@ fn is_in_loop_body(leaf: &SyntaxToken) -> bool { | |||
84 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { | 83 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { |
85 | break; | 84 | break; |
86 | } | 85 | } |
87 | let loop_body = visitor() | 86 | let loop_body = match_ast! { |
88 | .visit::<ast::ForExpr, _>(|it| it.loop_body()) | 87 | match node { |
89 | .visit::<ast::WhileExpr, _>(|it| it.loop_body()) | 88 | ast::ForExpr(it) => { it.loop_body() }, |
90 | .visit::<ast::LoopExpr, _>(|it| it.loop_body()) | 89 | ast::WhileExpr(it) => { it.loop_body() }, |
91 | .accept(&node); | 90 | ast::LoopExpr(it) => { it.loop_body() }, |
92 | if let Some(Some(body)) = loop_body { | 91 | _ => None, |
92 | } | ||
93 | }; | ||
94 | if let Some(body) = loop_body { | ||
93 | if leaf.text_range().is_subrange(&body.syntax().text_range()) { | 95 | if leaf.text_range().is_subrange(&body.syntax().text_range()) { |
94 | return true; | 96 | return true; |
95 | } | 97 | } |
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs index 60ae802c0..d0b1a8a2a 100644 --- a/crates/ra_ide_api/src/display/navigation_target.rs +++ b/crates/ra_ide_api/src/display/navigation_target.rs | |||
@@ -3,9 +3,8 @@ | |||
3 | use hir::{AssocItem, FieldSource, HasSource, ModuleSource}; | 3 | use hir::{AssocItem, FieldSource, HasSource, ModuleSource}; |
4 | use ra_db::{FileId, SourceDatabase}; | 4 | use ra_db::{FileId, SourceDatabase}; |
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | algo::visit::{visitor, Visitor}, | ||
7 | ast::{self, DocCommentsOwner}, | 6 | ast::{self, DocCommentsOwner}, |
8 | AstNode, AstPtr, SmolStr, | 7 | match_ast, AstNode, AstPtr, SmolStr, |
9 | SyntaxKind::{self, NAME}, | 8 | SyntaxKind::{self, NAME}, |
10 | SyntaxNode, TextRange, | 9 | SyntaxNode, TextRange, |
11 | }; | 10 | }; |
@@ -308,19 +307,22 @@ pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option | |||
308 | let parse = db.parse(symbol.file_id); | 307 | let parse = db.parse(symbol.file_id); |
309 | let node = symbol.ptr.to_node(parse.tree().syntax()); | 308 | let node = symbol.ptr.to_node(parse.tree().syntax()); |
310 | 309 | ||
311 | visitor() | 310 | match_ast! { |
312 | .visit(|it: ast::FnDef| it.doc_comment_text()) | 311 | match node { |
313 | .visit(|it: ast::StructDef| it.doc_comment_text()) | 312 | ast::FnDef(it) => { it.doc_comment_text() }, |
314 | .visit(|it: ast::EnumDef| it.doc_comment_text()) | 313 | ast::StructDef(it) => { it.doc_comment_text() }, |
315 | .visit(|it: ast::TraitDef| it.doc_comment_text()) | 314 | ast::EnumDef(it) => { it.doc_comment_text() }, |
316 | .visit(|it: ast::Module| it.doc_comment_text()) | 315 | ast::TraitDef(it) => { it.doc_comment_text() }, |
317 | .visit(|it: ast::TypeAliasDef| it.doc_comment_text()) | 316 | ast::Module(it) => { it.doc_comment_text() }, |
318 | .visit(|it: ast::ConstDef| it.doc_comment_text()) | 317 | ast::TypeAliasDef(it) => { it.doc_comment_text() }, |
319 | .visit(|it: ast::StaticDef| it.doc_comment_text()) | 318 | ast::ConstDef(it) => { it.doc_comment_text() }, |
320 | .visit(|it: ast::RecordFieldDef| it.doc_comment_text()) | 319 | ast::StaticDef(it) => { it.doc_comment_text() }, |
321 | .visit(|it: ast::EnumVariant| it.doc_comment_text()) | 320 | ast::RecordFieldDef(it) => { it.doc_comment_text() }, |
322 | .visit(|it: ast::MacroCall| it.doc_comment_text()) | 321 | ast::EnumVariant(it) => { it.doc_comment_text() }, |
323 | .accept(&node)? | 322 | ast::MacroCall(it) => { it.doc_comment_text() }, |
323 | _ => None, | ||
324 | } | ||
325 | } | ||
324 | } | 326 | } |
325 | 327 | ||
326 | /// Get a description of a symbol. | 328 | /// Get a description of a symbol. |
@@ -330,16 +332,19 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> | |||
330 | let parse = db.parse(symbol.file_id); | 332 | let parse = db.parse(symbol.file_id); |
331 | let node = symbol.ptr.to_node(parse.tree().syntax()); | 333 | let node = symbol.ptr.to_node(parse.tree().syntax()); |
332 | 334 | ||
333 | visitor() | 335 | match_ast! { |
334 | .visit(|node: ast::FnDef| node.short_label()) | 336 | match node { |
335 | .visit(|node: ast::StructDef| node.short_label()) | 337 | ast::FnDef(it) => { it.short_label() }, |
336 | .visit(|node: ast::EnumDef| node.short_label()) | 338 | ast::StructDef(it) => { it.short_label() }, |
337 | .visit(|node: ast::TraitDef| node.short_label()) | 339 | ast::EnumDef(it) => { it.short_label() }, |
338 | .visit(|node: ast::Module| node.short_label()) | 340 | ast::TraitDef(it) => { it.short_label() }, |
339 | .visit(|node: ast::TypeAliasDef| node.short_label()) | 341 | ast::Module(it) => { it.short_label() }, |
340 | .visit(|node: ast::ConstDef| node.short_label()) | 342 | ast::TypeAliasDef(it) => { it.short_label() }, |
341 | .visit(|node: ast::StaticDef| node.short_label()) | 343 | ast::ConstDef(it) => { it.short_label() }, |
342 | .visit(|node: ast::RecordFieldDef| node.short_label()) | 344 | ast::StaticDef(it) => { it.short_label() }, |
343 | .visit(|node: ast::EnumVariant| node.short_label()) | 345 | ast::RecordFieldDef(it) => { it.short_label() }, |
344 | .accept(&node)? | 346 | ast::EnumVariant(it) => { it.short_label() }, |
347 | _ => None, | ||
348 | } | ||
349 | } | ||
345 | } | 350 | } |
diff --git a/crates/ra_ide_api/src/display/structure.rs b/crates/ra_ide_api/src/display/structure.rs index 8815df747..ddd8b7b20 100644 --- a/crates/ra_ide_api/src/display/structure.rs +++ b/crates/ra_ide_api/src/display/structure.rs | |||
@@ -3,9 +3,8 @@ | |||
3 | use crate::TextRange; | 3 | use crate::TextRange; |
4 | 4 | ||
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | algo::visit::{visitor, Visitor}, | ||
7 | ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, | 6 | ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, |
8 | AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent, | 7 | match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent, |
9 | }; | 8 | }; |
10 | 9 | ||
11 | #[derive(Debug, Clone)] | 10 | #[derive(Debug, Clone)] |
@@ -101,63 +100,66 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> { | |||
101 | }) | 100 | }) |
102 | } | 101 | } |
103 | 102 | ||
104 | visitor() | 103 | match_ast! { |
105 | .visit(|fn_def: ast::FnDef| { | 104 | match node { |
106 | let mut detail = String::from("fn"); | 105 | ast::FnDef(it) => { |
107 | if let Some(type_param_list) = fn_def.type_param_list() { | 106 | let mut detail = String::from("fn"); |
108 | collapse_ws(type_param_list.syntax(), &mut detail); | 107 | if let Some(type_param_list) = it.type_param_list() { |
109 | } | 108 | collapse_ws(type_param_list.syntax(), &mut detail); |
110 | if let Some(param_list) = fn_def.param_list() { | 109 | } |
111 | collapse_ws(param_list.syntax(), &mut detail); | 110 | if let Some(param_list) = it.param_list() { |
112 | } | 111 | collapse_ws(param_list.syntax(), &mut detail); |
113 | if let Some(ret_type) = fn_def.ret_type() { | 112 | } |
114 | detail.push_str(" "); | 113 | if let Some(ret_type) = it.ret_type() { |
115 | collapse_ws(ret_type.syntax(), &mut detail); | 114 | detail.push_str(" "); |
116 | } | 115 | collapse_ws(ret_type.syntax(), &mut detail); |
117 | |||
118 | decl_with_detail(fn_def, Some(detail)) | ||
119 | }) | ||
120 | .visit(decl::<ast::StructDef>) | ||
121 | .visit(decl::<ast::EnumDef>) | ||
122 | .visit(decl::<ast::EnumVariant>) | ||
123 | .visit(decl::<ast::TraitDef>) | ||
124 | .visit(decl::<ast::Module>) | ||
125 | .visit(|td: ast::TypeAliasDef| { | ||
126 | let ty = td.type_ref(); | ||
127 | decl_with_type_ref(td, ty) | ||
128 | }) | ||
129 | .visit(decl_with_ascription::<ast::RecordFieldDef>) | ||
130 | .visit(decl_with_ascription::<ast::ConstDef>) | ||
131 | .visit(decl_with_ascription::<ast::StaticDef>) | ||
132 | .visit(|im: ast::ImplBlock| { | ||
133 | let target_type = im.target_type()?; | ||
134 | let target_trait = im.target_trait(); | ||
135 | let label = match target_trait { | ||
136 | None => format!("impl {}", target_type.syntax().text()), | ||
137 | Some(t) => { | ||
138 | format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),) | ||
139 | } | 116 | } |
140 | }; | ||
141 | 117 | ||
142 | let node = StructureNode { | 118 | decl_with_detail(it, Some(detail)) |
143 | parent: None, | 119 | }, |
144 | label, | 120 | ast::StructDef(it) => { decl(it) }, |
145 | navigation_range: target_type.syntax().text_range(), | 121 | ast::EnumDef(it) => { decl(it) }, |
146 | node_range: im.syntax().text_range(), | 122 | ast::EnumVariant(it) => { decl(it) }, |
147 | kind: im.syntax().kind(), | 123 | ast::TraitDef(it) => { decl(it) }, |
148 | detail: None, | 124 | ast::Module(it) => { decl(it) }, |
149 | deprecated: false, | 125 | ast::TypeAliasDef(it) => { |
150 | }; | 126 | let ty = it.type_ref(); |
151 | Some(node) | 127 | decl_with_type_ref(it, ty) |
152 | }) | 128 | }, |
153 | .visit(|mc: ast::MacroCall| { | 129 | ast::RecordFieldDef(it) => { decl_with_ascription(it) }, |
154 | let first_token = mc.syntax().first_token().unwrap(); | 130 | ast::ConstDef(it) => { decl_with_ascription(it) }, |
155 | if first_token.text().as_str() != "macro_rules" { | 131 | ast::StaticDef(it) => { decl_with_ascription(it) }, |
156 | return None; | 132 | ast::ImplBlock(it) => { |
157 | } | 133 | let target_type = it.target_type()?; |
158 | decl(mc) | 134 | let target_trait = it.target_trait(); |
159 | }) | 135 | let label = match target_trait { |
160 | .accept(&node)? | 136 | None => format!("impl {}", target_type.syntax().text()), |
137 | Some(t) => { | ||
138 | format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),) | ||
139 | } | ||
140 | }; | ||
141 | |||
142 | let node = StructureNode { | ||
143 | parent: None, | ||
144 | label, | ||
145 | navigation_range: target_type.syntax().text_range(), | ||
146 | node_range: it.syntax().text_range(), | ||
147 | kind: it.syntax().kind(), | ||
148 | detail: None, | ||
149 | deprecated: false, | ||
150 | }; | ||
151 | Some(node) | ||
152 | }, | ||
153 | ast::MacroCall(it) => { | ||
154 | let first_token = it.syntax().first_token().unwrap(); | ||
155 | if first_token.text().as_str() != "macro_rules" { | ||
156 | return None; | ||
157 | } | ||
158 | decl(it) | ||
159 | }, | ||
160 | _ => None, | ||
161 | } | ||
162 | } | ||
161 | } | 163 | } |
162 | 164 | ||
163 | #[cfg(test)] | 165 | #[cfg(test)] |
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 567d4a674..41a88314f 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -2,12 +2,9 @@ | |||
2 | 2 | ||
3 | use ra_db::{FileId, SourceDatabase}; | 3 | use ra_db::{FileId, SourceDatabase}; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | algo::{ | 5 | algo::find_node_at_offset, |
6 | find_node_at_offset, | ||
7 | visit::{visitor, Visitor}, | ||
8 | }, | ||
9 | ast::{self, DocCommentsOwner}, | 6 | ast::{self, DocCommentsOwner}, |
10 | AstNode, SyntaxNode, | 7 | match_ast, AstNode, SyntaxNode, |
11 | }; | 8 | }; |
12 | 9 | ||
13 | use crate::{ | 10 | use crate::{ |
@@ -114,91 +111,99 @@ pub(crate) fn name_definition( | |||
114 | } | 111 | } |
115 | 112 | ||
116 | fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> { | 113 | fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> { |
117 | visitor() | 114 | match_ast! { |
118 | .visit(|node: ast::StructDef| { | 115 | match node { |
119 | NavigationTarget::from_named( | 116 | ast::StructDef(it) => { |
120 | file_id, | 117 | Some(NavigationTarget::from_named( |
121 | &node, | 118 | file_id, |
122 | node.doc_comment_text(), | 119 | &it, |
123 | node.short_label(), | 120 | it.doc_comment_text(), |
124 | ) | 121 | it.short_label(), |
125 | }) | 122 | )) |
126 | .visit(|node: ast::EnumDef| { | 123 | }, |
127 | NavigationTarget::from_named( | 124 | ast::EnumDef(it) => { |
128 | file_id, | 125 | Some(NavigationTarget::from_named( |
129 | &node, | 126 | file_id, |
130 | node.doc_comment_text(), | 127 | &it, |
131 | node.short_label(), | 128 | it.doc_comment_text(), |
132 | ) | 129 | it.short_label(), |
133 | }) | 130 | )) |
134 | .visit(|node: ast::EnumVariant| { | 131 | }, |
135 | NavigationTarget::from_named( | 132 | ast::EnumVariant(it) => { |
136 | file_id, | 133 | Some(NavigationTarget::from_named( |
137 | &node, | 134 | file_id, |
138 | node.doc_comment_text(), | 135 | &it, |
139 | node.short_label(), | 136 | it.doc_comment_text(), |
140 | ) | 137 | it.short_label(), |
141 | }) | 138 | )) |
142 | .visit(|node: ast::FnDef| { | 139 | }, |
143 | NavigationTarget::from_named( | 140 | ast::FnDef(it) => { |
144 | file_id, | 141 | Some(NavigationTarget::from_named( |
145 | &node, | 142 | file_id, |
146 | node.doc_comment_text(), | 143 | &it, |
147 | node.short_label(), | 144 | it.doc_comment_text(), |
148 | ) | 145 | it.short_label(), |
149 | }) | 146 | )) |
150 | .visit(|node: ast::TypeAliasDef| { | 147 | }, |
151 | NavigationTarget::from_named( | 148 | ast::TypeAliasDef(it) => { |
152 | file_id, | 149 | Some(NavigationTarget::from_named( |
153 | &node, | 150 | file_id, |
154 | node.doc_comment_text(), | 151 | &it, |
155 | node.short_label(), | 152 | it.doc_comment_text(), |
156 | ) | 153 | it.short_label(), |
157 | }) | 154 | )) |
158 | .visit(|node: ast::ConstDef| { | 155 | }, |
159 | NavigationTarget::from_named( | 156 | ast::ConstDef(it) => { |
160 | file_id, | 157 | Some(NavigationTarget::from_named( |
161 | &node, | 158 | file_id, |
162 | node.doc_comment_text(), | 159 | &it, |
163 | node.short_label(), | 160 | it.doc_comment_text(), |
164 | ) | 161 | it.short_label(), |
165 | }) | 162 | )) |
166 | .visit(|node: ast::StaticDef| { | 163 | }, |
167 | NavigationTarget::from_named( | 164 | ast::StaticDef(it) => { |
168 | file_id, | 165 | Some(NavigationTarget::from_named( |
169 | &node, | 166 | file_id, |
170 | node.doc_comment_text(), | 167 | &it, |
171 | node.short_label(), | 168 | it.doc_comment_text(), |
172 | ) | 169 | it.short_label(), |
173 | }) | 170 | )) |
174 | .visit(|node: ast::TraitDef| { | 171 | }, |
175 | NavigationTarget::from_named( | 172 | ast::TraitDef(it) => { |
176 | file_id, | 173 | Some(NavigationTarget::from_named( |
177 | &node, | 174 | file_id, |
178 | node.doc_comment_text(), | 175 | &it, |
179 | node.short_label(), | 176 | it.doc_comment_text(), |
180 | ) | 177 | it.short_label(), |
181 | }) | 178 | )) |
182 | .visit(|node: ast::RecordFieldDef| { | 179 | }, |
183 | NavigationTarget::from_named( | 180 | ast::RecordFieldDef(it) => { |
184 | file_id, | 181 | Some(NavigationTarget::from_named( |
185 | &node, | 182 | file_id, |
186 | node.doc_comment_text(), | 183 | &it, |
187 | node.short_label(), | 184 | it.doc_comment_text(), |
188 | ) | 185 | it.short_label(), |
189 | }) | 186 | )) |
190 | .visit(|node: ast::Module| { | 187 | }, |
191 | NavigationTarget::from_named( | 188 | ast::Module(it) => { |
192 | file_id, | 189 | Some(NavigationTarget::from_named( |
193 | &node, | 190 | file_id, |
194 | node.doc_comment_text(), | 191 | &it, |
195 | node.short_label(), | 192 | it.doc_comment_text(), |
196 | ) | 193 | it.short_label(), |
197 | }) | 194 | )) |
198 | .visit(|node: ast::MacroCall| { | 195 | }, |
199 | NavigationTarget::from_named(file_id, &node, node.doc_comment_text(), None) | 196 | ast::MacroCall(it) => { |
200 | }) | 197 | Some(NavigationTarget::from_named( |
201 | .accept(node) | 198 | file_id, |
199 | &it, | ||
200 | it.doc_comment_text(), | ||
201 | None, | ||
202 | )) | ||
203 | }, | ||
204 | _ => None, | ||
205 | } | ||
206 | } | ||
202 | } | 207 | } |
203 | 208 | ||
204 | #[cfg(test)] | 209 | #[cfg(test)] |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 200b57679..24b161c5c 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -3,12 +3,9 @@ | |||
3 | use hir::{Adt, HasSource, HirDisplay}; | 3 | use hir::{Adt, HasSource, HirDisplay}; |
4 | use ra_db::SourceDatabase; | 4 | use ra_db::SourceDatabase; |
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | algo::{ | 6 | algo::{ancestors_at_offset, find_covering_element, find_node_at_offset}, |
7 | ancestors_at_offset, find_covering_element, find_node_at_offset, | ||
8 | visit::{visitor, Visitor}, | ||
9 | }, | ||
10 | ast::{self, DocCommentsOwner}, | 7 | ast::{self, DocCommentsOwner}, |
11 | AstNode, | 8 | match_ast, AstNode, |
12 | }; | 9 | }; |
13 | 10 | ||
14 | use crate::{ | 11 | use crate::{ |
@@ -178,37 +175,45 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
178 | } | 175 | } |
179 | } else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) { | 176 | } else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) { |
180 | if let Some(parent) = name.syntax().parent() { | 177 | if let Some(parent) = name.syntax().parent() { |
181 | let text = visitor() | 178 | let text = match_ast! { |
182 | .visit(|node: ast::StructDef| { | 179 | match parent { |
183 | hover_text(node.doc_comment_text(), node.short_label()) | 180 | ast::StructDef(it) => { |
184 | }) | 181 | hover_text(it.doc_comment_text(), it.short_label()) |
185 | .visit(|node: ast::EnumDef| hover_text(node.doc_comment_text(), node.short_label())) | 182 | }, |
186 | .visit(|node: ast::EnumVariant| { | 183 | ast::EnumDef(it) => { |
187 | hover_text(node.doc_comment_text(), node.short_label()) | 184 | hover_text(it.doc_comment_text(), it.short_label()) |
188 | }) | 185 | }, |
189 | .visit(|node: ast::FnDef| hover_text(node.doc_comment_text(), node.short_label())) | 186 | ast::EnumVariant(it) => { |
190 | .visit(|node: ast::TypeAliasDef| { | 187 | hover_text(it.doc_comment_text(), it.short_label()) |
191 | hover_text(node.doc_comment_text(), node.short_label()) | 188 | }, |
192 | }) | 189 | ast::FnDef(it) => { |
193 | .visit(|node: ast::ConstDef| { | 190 | hover_text(it.doc_comment_text(), it.short_label()) |
194 | hover_text(node.doc_comment_text(), node.short_label()) | 191 | }, |
195 | }) | 192 | ast::TypeAliasDef(it) => { |
196 | .visit(|node: ast::StaticDef| { | 193 | hover_text(it.doc_comment_text(), it.short_label()) |
197 | hover_text(node.doc_comment_text(), node.short_label()) | 194 | }, |
198 | }) | 195 | ast::ConstDef(it) => { |
199 | .visit(|node: ast::TraitDef| { | 196 | hover_text(it.doc_comment_text(), it.short_label()) |
200 | hover_text(node.doc_comment_text(), node.short_label()) | 197 | }, |
201 | }) | 198 | ast::StaticDef(it) => { |
202 | .visit(|node: ast::RecordFieldDef| { | 199 | hover_text(it.doc_comment_text(), it.short_label()) |
203 | hover_text(node.doc_comment_text(), node.short_label()) | 200 | }, |
204 | }) | 201 | ast::TraitDef(it) => { |
205 | .visit(|node: ast::Module| hover_text(node.doc_comment_text(), node.short_label())) | 202 | hover_text(it.doc_comment_text(), it.short_label()) |
206 | .visit(|node: ast::MacroCall| hover_text(node.doc_comment_text(), None)) | 203 | }, |
207 | .accept(&parent); | 204 | ast::RecordFieldDef(it) => { |
208 | 205 | hover_text(it.doc_comment_text(), it.short_label()) | |
209 | if let Some(text) = text { | 206 | }, |
210 | res.extend(text); | 207 | ast::Module(it) => { |
211 | } | 208 | hover_text(it.doc_comment_text(), it.short_label()) |
209 | }, | ||
210 | ast::MacroCall(it) => { | ||
211 | hover_text(it.doc_comment_text(), None) | ||
212 | }, | ||
213 | _ => None, | ||
214 | } | ||
215 | }; | ||
216 | res.extend(text); | ||
212 | } | 217 | } |
213 | 218 | ||
214 | if !res.is_empty() && range.is_none() { | 219 | if !res.is_empty() && range.is_none() { |
diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs index 9b45575f8..f1c0dc164 100644 --- a/crates/ra_ide_api/src/inlay_hints.rs +++ b/crates/ra_ide_api/src/inlay_hints.rs | |||
@@ -3,9 +3,8 @@ | |||
3 | use crate::{db::RootDatabase, FileId}; | 3 | use crate::{db::RootDatabase, FileId}; |
4 | use hir::{HirDisplay, SourceAnalyzer, Ty}; | 4 | use hir::{HirDisplay, SourceAnalyzer, Ty}; |
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | algo::visit::{visitor, Visitor}, | ||
7 | ast::{self, AstNode, TypeAscriptionOwner}, | 6 | ast::{self, AstNode, TypeAscriptionOwner}, |
8 | SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, | 7 | match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, |
9 | }; | 8 | }; |
10 | 9 | ||
11 | #[derive(Debug, PartialEq, Eq)] | 10 | #[derive(Debug, PartialEq, Eq)] |
@@ -33,55 +32,58 @@ fn get_inlay_hints( | |||
33 | file_id: FileId, | 32 | file_id: FileId, |
34 | node: &SyntaxNode, | 33 | node: &SyntaxNode, |
35 | ) -> Option<Vec<InlayHint>> { | 34 | ) -> Option<Vec<InlayHint>> { |
36 | visitor() | 35 | match_ast! { |
37 | .visit(|let_statement: ast::LetStmt| { | 36 | match node { |
38 | if let_statement.ascribed_type().is_some() { | 37 | ast::LetStmt(it) => { |
39 | return None; | 38 | if it.ascribed_type().is_some() { |
40 | } | 39 | return None; |
41 | let pat = let_statement.pat()?; | 40 | } |
42 | let analyzer = SourceAnalyzer::new(db, file_id, let_statement.syntax(), None); | 41 | let pat = it.pat()?; |
43 | Some(get_pat_type_hints(db, &analyzer, pat, false)) | 42 | let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None); |
44 | }) | 43 | Some(get_pat_type_hints(db, &analyzer, pat, false)) |
45 | .visit(|closure_parameter: ast::LambdaExpr| { | 44 | }, |
46 | let analyzer = SourceAnalyzer::new(db, file_id, closure_parameter.syntax(), None); | 45 | ast::LambdaExpr(it) => { |
47 | closure_parameter.param_list().map(|param_list| { | 46 | let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None); |
48 | param_list | 47 | it.param_list().map(|param_list| { |
49 | .params() | 48 | param_list |
50 | .filter(|closure_param| closure_param.ascribed_type().is_none()) | 49 | .params() |
51 | .filter_map(|closure_param| closure_param.pat()) | 50 | .filter(|closure_param| closure_param.ascribed_type().is_none()) |
52 | .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, false)) | 51 | .filter_map(|closure_param| closure_param.pat()) |
53 | .flatten() | 52 | .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, false)) |
54 | .collect() | 53 | .flatten() |
55 | }) | 54 | .collect() |
56 | }) | 55 | }) |
57 | .visit(|for_expression: ast::ForExpr| { | 56 | }, |
58 | let pat = for_expression.pat()?; | 57 | ast::ForExpr(it) => { |
59 | let analyzer = SourceAnalyzer::new(db, file_id, for_expression.syntax(), None); | 58 | let pat = it.pat()?; |
60 | Some(get_pat_type_hints(db, &analyzer, pat, false)) | 59 | let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None); |
61 | }) | 60 | Some(get_pat_type_hints(db, &analyzer, pat, false)) |
62 | .visit(|if_expr: ast::IfExpr| { | 61 | }, |
63 | let pat = if_expr.condition()?.pat()?; | 62 | ast::IfExpr(it) => { |
64 | let analyzer = SourceAnalyzer::new(db, file_id, if_expr.syntax(), None); | 63 | let pat = it.condition()?.pat()?; |
65 | Some(get_pat_type_hints(db, &analyzer, pat, true)) | 64 | let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None); |
66 | }) | 65 | Some(get_pat_type_hints(db, &analyzer, pat, true)) |
67 | .visit(|while_expr: ast::WhileExpr| { | 66 | }, |
68 | let pat = while_expr.condition()?.pat()?; | 67 | ast::WhileExpr(it) => { |
69 | let analyzer = SourceAnalyzer::new(db, file_id, while_expr.syntax(), None); | 68 | let pat = it.condition()?.pat()?; |
70 | Some(get_pat_type_hints(db, &analyzer, pat, true)) | 69 | let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None); |
71 | }) | 70 | Some(get_pat_type_hints(db, &analyzer, pat, true)) |
72 | .visit(|match_arm_list: ast::MatchArmList| { | 71 | }, |
73 | let analyzer = SourceAnalyzer::new(db, file_id, match_arm_list.syntax(), None); | 72 | ast::MatchArmList(it) => { |
74 | Some( | 73 | let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None); |
75 | match_arm_list | 74 | Some( |
76 | .arms() | 75 | it |
77 | .map(|match_arm| match_arm.pats()) | 76 | .arms() |
78 | .flatten() | 77 | .map(|match_arm| match_arm.pats()) |
79 | .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, true)) | 78 | .flatten() |
80 | .flatten() | 79 | .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, true)) |
81 | .collect(), | 80 | .flatten() |
82 | ) | 81 | .collect(), |
83 | }) | 82 | ) |
84 | .accept(&node)? | 83 | }, |
84 | _ => None, | ||
85 | } | ||
86 | } | ||
85 | } | 87 | } |
86 | 88 | ||
87 | fn get_pat_type_hints( | 89 | fn get_pat_type_hints( |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 44d1ec77b..24f1b91f6 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -49,6 +49,7 @@ mod test_utils; | |||
49 | 49 | ||
50 | use std::sync::Arc; | 50 | use std::sync::Arc; |
51 | 51 | ||
52 | use ra_cfg::CfgOptions; | ||
52 | use ra_db::{ | 53 | use ra_db::{ |
53 | salsa::{self, ParallelDatabase}, | 54 | salsa::{self, ParallelDatabase}, |
54 | CheckCanceled, SourceDatabase, | 55 | CheckCanceled, SourceDatabase, |
@@ -322,7 +323,10 @@ impl Analysis { | |||
322 | change.add_root(source_root, true); | 323 | change.add_root(source_root, true); |
323 | let mut crate_graph = CrateGraph::default(); | 324 | let mut crate_graph = CrateGraph::default(); |
324 | let file_id = FileId(0); | 325 | let file_id = FileId(0); |
325 | crate_graph.add_crate_root(file_id, Edition::Edition2018); | 326 | // FIXME: cfg options |
327 | // Default to enable test for single file. | ||
328 | let cfg_options = CfgOptions::default().atom("test".into()); | ||
329 | crate_graph.add_crate_root(file_id, Edition::Edition2018, cfg_options); | ||
326 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); | 330 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); |
327 | change.set_crate_graph(crate_graph); | 331 | change.set_crate_graph(crate_graph); |
328 | host.apply_change(change); | 332 | host.apply_change(change); |
diff --git a/crates/ra_ide_api/src/mock_analysis.rs b/crates/ra_ide_api/src/mock_analysis.rs index 16870c7ae..80b71894c 100644 --- a/crates/ra_ide_api/src/mock_analysis.rs +++ b/crates/ra_ide_api/src/mock_analysis.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use ra_cfg::CfgOptions; | ||
5 | use relative_path::RelativePathBuf; | 6 | use relative_path::RelativePathBuf; |
6 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; | 7 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; |
7 | 8 | ||
@@ -93,10 +94,11 @@ impl MockAnalysis { | |||
93 | assert!(path.starts_with('/')); | 94 | assert!(path.starts_with('/')); |
94 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); | 95 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); |
95 | let file_id = FileId(i as u32 + 1); | 96 | let file_id = FileId(i as u32 + 1); |
97 | let cfg_options = CfgOptions::default(); | ||
96 | if path == "/lib.rs" || path == "/main.rs" { | 98 | if path == "/lib.rs" || path == "/main.rs" { |
97 | root_crate = Some(crate_graph.add_crate_root(file_id, Edition2018)); | 99 | root_crate = Some(crate_graph.add_crate_root(file_id, Edition2018, cfg_options)); |
98 | } else if path.ends_with("/lib.rs") { | 100 | } else if path.ends_with("/lib.rs") { |
99 | let other_crate = crate_graph.add_crate_root(file_id, Edition2018); | 101 | let other_crate = crate_graph.add_crate_root(file_id, Edition2018, cfg_options); |
100 | let crate_name = path.parent().unwrap().file_name().unwrap(); | 102 | let crate_name = path.parent().unwrap().file_name().unwrap(); |
101 | if let Some(root_crate) = root_crate { | 103 | if let Some(root_crate) = root_crate { |
102 | crate_graph.add_dep(root_crate, crate_name.into(), other_crate).unwrap(); | 104 | crate_graph.add_dep(root_crate, crate_name.into(), other_crate).unwrap(); |
diff --git a/crates/ra_ide_api/src/parent_module.rs b/crates/ra_ide_api/src/parent_module.rs index c85f1d0d0..566509849 100644 --- a/crates/ra_ide_api/src/parent_module.rs +++ b/crates/ra_ide_api/src/parent_module.rs | |||
@@ -41,6 +41,7 @@ mod tests { | |||
41 | AnalysisChange, CrateGraph, | 41 | AnalysisChange, CrateGraph, |
42 | Edition::Edition2018, | 42 | Edition::Edition2018, |
43 | }; | 43 | }; |
44 | use ra_cfg::CfgOptions; | ||
44 | 45 | ||
45 | #[test] | 46 | #[test] |
46 | fn test_resolve_parent_module() { | 47 | fn test_resolve_parent_module() { |
@@ -88,7 +89,7 @@ mod tests { | |||
88 | assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); | 89 | assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); |
89 | 90 | ||
90 | let mut crate_graph = CrateGraph::default(); | 91 | let mut crate_graph = CrateGraph::default(); |
91 | let crate_id = crate_graph.add_crate_root(root_file, Edition2018); | 92 | let crate_id = crate_graph.add_crate_root(root_file, Edition2018, CfgOptions::default()); |
92 | let mut change = AnalysisChange::new(); | 93 | let mut change = AnalysisChange::new(); |
93 | change.set_crate_graph(crate_graph); | 94 | change.set_crate_graph(crate_graph); |
94 | host.apply_change(change); | 95 | host.apply_change(change); |
diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs index 02cdfbc60..797e9926f 100644 --- a/crates/ra_ide_api/src/symbol_index.rs +++ b/crates/ra_ide_api/src/symbol_index.rs | |||
@@ -32,9 +32,8 @@ use ra_db::{ | |||
32 | SourceDatabase, SourceRootId, | 32 | SourceDatabase, SourceRootId, |
33 | }; | 33 | }; |
34 | use ra_syntax::{ | 34 | use ra_syntax::{ |
35 | algo::visit::{visitor, Visitor}, | ||
36 | ast::{self, NameOwner}, | 35 | ast::{self, NameOwner}, |
37 | AstNode, Parse, SmolStr, SourceFile, | 36 | match_ast, AstNode, Parse, SmolStr, SourceFile, |
38 | SyntaxKind::{self, *}, | 37 | SyntaxKind::{self, *}, |
39 | SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent, | 38 | SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent, |
40 | }; | 39 | }; |
@@ -306,16 +305,19 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { | |||
306 | 305 | ||
307 | Some((name, ptr, name_range)) | 306 | Some((name, ptr, name_range)) |
308 | } | 307 | } |
309 | visitor() | 308 | match_ast! { |
310 | .visit(decl::<ast::FnDef>) | 309 | match node { |
311 | .visit(decl::<ast::StructDef>) | 310 | ast::FnDef(it) => { decl(it) }, |
312 | .visit(decl::<ast::EnumDef>) | 311 | ast::StructDef(it) => { decl(it) }, |
313 | .visit(decl::<ast::TraitDef>) | 312 | ast::EnumDef(it) => { decl(it) }, |
314 | .visit(decl::<ast::Module>) | 313 | ast::TraitDef(it) => { decl(it) }, |
315 | .visit(decl::<ast::TypeAliasDef>) | 314 | ast::Module(it) => { decl(it) }, |
316 | .visit(decl::<ast::ConstDef>) | 315 | ast::TypeAliasDef(it) => { decl(it) }, |
317 | .visit(decl::<ast::StaticDef>) | 316 | ast::ConstDef(it) => { decl(it) }, |
318 | .accept(node)? | 317 | ast::StaticDef(it) => { decl(it) }, |
318 | _ => None, | ||
319 | } | ||
320 | } | ||
319 | } | 321 | } |
320 | 322 | ||
321 | fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> { | 323 | fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> { |
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index 677d81835..aedc55a95 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml | |||
@@ -19,6 +19,7 @@ jod-thread = "0.1.0" | |||
19 | ra_vfs = "0.4.0" | 19 | ra_vfs = "0.4.0" |
20 | ra_syntax = { path = "../ra_syntax" } | 20 | ra_syntax = { path = "../ra_syntax" } |
21 | ra_db = { path = "../ra_db" } | 21 | ra_db = { path = "../ra_db" } |
22 | ra_cfg = { path = "../ra_cfg" } | ||
22 | ra_text_edit = { path = "../ra_text_edit" } | 23 | ra_text_edit = { path = "../ra_text_edit" } |
23 | ra_ide_api = { path = "../ra_ide_api" } | 24 | ra_ide_api = { path = "../ra_ide_api" } |
24 | lsp-server = "0.2.0" | 25 | lsp-server = "0.2.0" |
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index b55046ec9..27da751ab 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs | |||
@@ -13,7 +13,7 @@ use ra_ide_api::{ | |||
13 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, | 13 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, |
14 | SourceRootId, | 14 | SourceRootId, |
15 | }; | 15 | }; |
16 | use ra_project_model::ProjectWorkspace; | 16 | use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace}; |
17 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; | 17 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; |
18 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; | 18 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; |
19 | use relative_path::RelativePathBuf; | 19 | use relative_path::RelativePathBuf; |
@@ -97,6 +97,10 @@ impl WorldState { | |||
97 | change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); | 97 | change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); |
98 | } | 98 | } |
99 | 99 | ||
100 | // FIXME: Read default cfgs from config | ||
101 | let default_cfg_options = | ||
102 | get_rustc_cfg_options().atom("test".into()).atom("debug_assertion".into()); | ||
103 | |||
100 | // Create crate graph from all the workspaces | 104 | // Create crate graph from all the workspaces |
101 | let mut crate_graph = CrateGraph::default(); | 105 | let mut crate_graph = CrateGraph::default(); |
102 | let mut load = |path: &std::path::Path| { | 106 | let mut load = |path: &std::path::Path| { |
@@ -104,7 +108,7 @@ impl WorldState { | |||
104 | vfs_file.map(|f| FileId(f.0)) | 108 | vfs_file.map(|f| FileId(f.0)) |
105 | }; | 109 | }; |
106 | for ws in workspaces.iter() { | 110 | for ws in workspaces.iter() { |
107 | let (graph, crate_names) = ws.to_crate_graph(&mut load); | 111 | let (graph, crate_names) = ws.to_crate_graph(&default_cfg_options, &mut load); |
108 | let shift = crate_graph.extend(graph); | 112 | let shift = crate_graph.extend(graph); |
109 | for (crate_id, name) in crate_names { | 113 | for (crate_id, name) in crate_names { |
110 | change.set_debug_crate_name(crate_id.shift(shift), name) | 114 | change.set_debug_crate_name(crate_id.shift(shift), name) |
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index 152681062..2ba82ab05 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs | |||
@@ -286,7 +286,13 @@ fn test_missing_module_code_action_in_json_project() { | |||
286 | 286 | ||
287 | let project = json!({ | 287 | let project = json!({ |
288 | "roots": [path], | 288 | "roots": [path], |
289 | "crates": [ { "root_module": path.join("src/lib.rs"), "deps": [], "edition": "2015" } ] | 289 | "crates": [ { |
290 | "root_module": path.join("src/lib.rs"), | ||
291 | "deps": [], | ||
292 | "edition": "2015", | ||
293 | "atom_cfgs": [], | ||
294 | "key_value_cfgs": {} | ||
295 | } ] | ||
290 | }); | 296 | }); |
291 | 297 | ||
292 | let code = format!( | 298 | let code = format!( |
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs index c2a2060eb..45f2e3de4 100644 --- a/crates/ra_parser/src/grammar/expressions.rs +++ b/crates/ra_parser/src/grammar/expressions.rs | |||
@@ -344,7 +344,13 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> | |||
344 | // } | 344 | // } |
345 | // | 345 | // |
346 | let (lhs, blocklike) = atom::atom_expr(p, r)?; | 346 | let (lhs, blocklike) = atom::atom_expr(p, r)?; |
347 | return Some(postfix_expr(p, lhs, blocklike, !(r.prefer_stmt && blocklike.is_block()))); | 347 | return Some(postfix_expr( |
348 | p, | ||
349 | lhs, | ||
350 | blocklike, | ||
351 | !(r.prefer_stmt && blocklike.is_block()), | ||
352 | r.forbid_structs, | ||
353 | )); | ||
348 | } | 354 | } |
349 | }; | 355 | }; |
350 | // parse the interior of the unary expression | 356 | // parse the interior of the unary expression |
@@ -360,6 +366,7 @@ fn postfix_expr( | |||
360 | // `while true {break}; ();` | 366 | // `while true {break}; ();` |
361 | mut block_like: BlockLike, | 367 | mut block_like: BlockLike, |
362 | mut allow_calls: bool, | 368 | mut allow_calls: bool, |
369 | forbid_structs: bool, | ||
363 | ) -> (CompletedMarker, BlockLike) { | 370 | ) -> (CompletedMarker, BlockLike) { |
364 | loop { | 371 | loop { |
365 | lhs = match p.current() { | 372 | lhs = match p.current() { |
@@ -373,7 +380,7 @@ fn postfix_expr( | |||
373 | // } | 380 | // } |
374 | T!['('] if allow_calls => call_expr(p, lhs), | 381 | T!['('] if allow_calls => call_expr(p, lhs), |
375 | T!['['] if allow_calls => index_expr(p, lhs), | 382 | T!['['] if allow_calls => index_expr(p, lhs), |
376 | T![.] => match postfix_dot_expr(p, lhs) { | 383 | T![.] => match postfix_dot_expr(p, lhs, forbid_structs) { |
377 | Ok(it) => it, | 384 | Ok(it) => it, |
378 | Err(it) => { | 385 | Err(it) => { |
379 | lhs = it; | 386 | lhs = it; |
@@ -391,6 +398,7 @@ fn postfix_expr( | |||
391 | fn postfix_dot_expr( | 398 | fn postfix_dot_expr( |
392 | p: &mut Parser, | 399 | p: &mut Parser, |
393 | lhs: CompletedMarker, | 400 | lhs: CompletedMarker, |
401 | forbid_structs: bool, | ||
394 | ) -> Result<CompletedMarker, CompletedMarker> { | 402 | ) -> Result<CompletedMarker, CompletedMarker> { |
395 | assert!(p.at(T![.])); | 403 | assert!(p.at(T![.])); |
396 | if p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth_at(2, T![::])) { | 404 | if p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth_at(2, T![::])) { |
@@ -411,10 +419,17 @@ fn postfix_expr( | |||
411 | } | 419 | } |
412 | 420 | ||
413 | // test postfix_range | 421 | // test postfix_range |
414 | // fn foo() { let x = 1..; } | 422 | // fn foo() { |
415 | for &(op, la) in [(T![..=], 3), (T![..], 2)].iter() { | 423 | // let x = 1..; |
424 | // match 1.. { _ => () }; | ||
425 | // match a.b()..S { _ => () }; | ||
426 | // } | ||
427 | for &(op, la) in &[(T![..=], 3), (T![..], 2)] { | ||
416 | if p.at(op) { | 428 | if p.at(op) { |
417 | return if EXPR_FIRST.contains(p.nth(la)) { | 429 | let next_token = p.nth(la); |
430 | let has_trailing_expression = | ||
431 | !(forbid_structs && next_token == T!['{']) && EXPR_FIRST.contains(next_token); | ||
432 | return if has_trailing_expression { | ||
418 | Err(lhs) | 433 | Err(lhs) |
419 | } else { | 434 | } else { |
420 | let m = lhs.precede(p); | 435 | let m = lhs.precede(p); |
diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs index a52bdb3ea..7454005c4 100644 --- a/crates/ra_parser/src/grammar/expressions/atom.rs +++ b/crates/ra_parser/src/grammar/expressions/atom.rs | |||
@@ -121,11 +121,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar | |||
121 | // break; | 121 | // break; |
122 | // } | 122 | // } |
123 | // } | 123 | // } |
124 | if r.forbid_structs { | 124 | block_expr(p, None) |
125 | return None; | ||
126 | } else { | ||
127 | block_expr(p, None) | ||
128 | } | ||
129 | } | 125 | } |
130 | T![return] => return_expr(p), | 126 | T![return] => return_expr(p), |
131 | T![continue] => continue_expr(p), | 127 | T![continue] => continue_expr(p), |
@@ -261,6 +257,7 @@ fn lambda_expr(p: &mut Parser) -> CompletedMarker { | |||
261 | // if true {} else {}; | 257 | // if true {} else {}; |
262 | // if true {} else if false {} else {}; | 258 | // if true {} else if false {} else {}; |
263 | // if S {}; | 259 | // if S {}; |
260 | // if { true } { } else { }; | ||
264 | // } | 261 | // } |
265 | fn if_expr(p: &mut Parser) -> CompletedMarker { | 262 | fn if_expr(p: &mut Parser) -> CompletedMarker { |
266 | assert!(p.at(T![if])); | 263 | assert!(p.at(T![if])); |
@@ -309,6 +306,7 @@ fn loop_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | |||
309 | // fn foo() { | 306 | // fn foo() { |
310 | // while true {}; | 307 | // while true {}; |
311 | // while let Some(x) = it.next() {}; | 308 | // while let Some(x) = it.next() {}; |
309 | // while { true } {}; | ||
312 | // } | 310 | // } |
313 | fn while_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | 311 | fn while_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { |
314 | assert!(p.at(T![while])); | 312 | assert!(p.at(T![while])); |
@@ -356,6 +354,8 @@ fn cond(p: &mut Parser) { | |||
356 | // fn foo() { | 354 | // fn foo() { |
357 | // match () { }; | 355 | // match () { }; |
358 | // match S {}; | 356 | // match S {}; |
357 | // match { } { _ => () }; | ||
358 | // match { S {} } {}; | ||
359 | // } | 359 | // } |
360 | fn match_expr(p: &mut Parser) -> CompletedMarker { | 360 | fn match_expr(p: &mut Parser) -> CompletedMarker { |
361 | assert!(p.at(T![match])); | 361 | assert!(p.at(T![match])); |
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index ae6b91aa6..a65100031 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml | |||
@@ -12,6 +12,7 @@ cargo_metadata = "0.8.2" | |||
12 | 12 | ||
13 | ra_arena = { path = "../ra_arena" } | 13 | ra_arena = { path = "../ra_arena" } |
14 | ra_db = { path = "../ra_db" } | 14 | ra_db = { path = "../ra_db" } |
15 | ra_cfg = { path = "../ra_cfg" } | ||
15 | 16 | ||
16 | serde = { version = "1.0.89", features = ["derive"] } | 17 | serde = { version = "1.0.89", features = ["derive"] } |
17 | serde_json = "1.0.39" | 18 | serde_json = "1.0.39" |
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 805eaa178..28dadea9d 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -39,6 +39,7 @@ struct PackageData { | |||
39 | is_member: bool, | 39 | is_member: bool, |
40 | dependencies: Vec<PackageDependency>, | 40 | dependencies: Vec<PackageDependency>, |
41 | edition: Edition, | 41 | edition: Edition, |
42 | features: Vec<String>, | ||
42 | } | 43 | } |
43 | 44 | ||
44 | #[derive(Debug, Clone)] | 45 | #[derive(Debug, Clone)] |
@@ -91,6 +92,9 @@ impl Package { | |||
91 | pub fn edition(self, ws: &CargoWorkspace) -> Edition { | 92 | pub fn edition(self, ws: &CargoWorkspace) -> Edition { |
92 | ws.packages[self].edition | 93 | ws.packages[self].edition |
93 | } | 94 | } |
95 | pub fn features(self, ws: &CargoWorkspace) -> &[String] { | ||
96 | &ws.packages[self].features | ||
97 | } | ||
94 | pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a { | 98 | pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a { |
95 | ws.packages[self].targets.iter().cloned() | 99 | ws.packages[self].targets.iter().cloned() |
96 | } | 100 | } |
@@ -144,6 +148,7 @@ impl CargoWorkspace { | |||
144 | is_member, | 148 | is_member, |
145 | edition: Edition::from_string(&meta_pkg.edition), | 149 | edition: Edition::from_string(&meta_pkg.edition), |
146 | dependencies: Vec::new(), | 150 | dependencies: Vec::new(), |
151 | features: Vec::new(), | ||
147 | }); | 152 | }); |
148 | let pkg_data = &mut packages[pkg]; | 153 | let pkg_data = &mut packages[pkg]; |
149 | pkg_by_id.insert(meta_pkg.id.clone(), pkg); | 154 | pkg_by_id.insert(meta_pkg.id.clone(), pkg); |
@@ -164,6 +169,7 @@ impl CargoWorkspace { | |||
164 | let dep = PackageDependency { name: dep_node.name, pkg: pkg_by_id[&dep_node.pkg] }; | 169 | let dep = PackageDependency { name: dep_node.name, pkg: pkg_by_id[&dep_node.pkg] }; |
165 | packages[source].dependencies.push(dep); | 170 | packages[source].dependencies.push(dep); |
166 | } | 171 | } |
172 | packages[source].features.extend(node.features); | ||
167 | } | 173 | } |
168 | 174 | ||
169 | Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root }) | 175 | Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root }) |
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index 54ddca2cb..1bacb1d09 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | use std::path::PathBuf; | 3 | use std::path::PathBuf; |
4 | 4 | ||
5 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
5 | use serde::Deserialize; | 6 | use serde::Deserialize; |
6 | 7 | ||
7 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in | 8 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in |
@@ -19,6 +20,8 @@ pub struct Crate { | |||
19 | pub(crate) root_module: PathBuf, | 20 | pub(crate) root_module: PathBuf, |
20 | pub(crate) edition: Edition, | 21 | pub(crate) edition: Edition, |
21 | pub(crate) deps: Vec<Dep>, | 22 | pub(crate) deps: Vec<Dep>, |
23 | pub(crate) atom_cfgs: FxHashSet<String>, | ||
24 | pub(crate) key_value_cfgs: FxHashMap<String, String>, | ||
22 | } | 25 | } |
23 | 26 | ||
24 | #[derive(Clone, Copy, Debug, Deserialize)] | 27 | #[derive(Clone, Copy, Debug, Deserialize)] |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 5d3078598..640a5ebd3 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -9,8 +9,10 @@ use std::{ | |||
9 | fs::File, | 9 | fs::File, |
10 | io::BufReader, | 10 | io::BufReader, |
11 | path::{Path, PathBuf}, | 11 | path::{Path, PathBuf}, |
12 | process::Command, | ||
12 | }; | 13 | }; |
13 | 14 | ||
15 | use ra_cfg::CfgOptions; | ||
14 | use ra_db::{CrateGraph, CrateId, Edition, FileId}; | 16 | use ra_db::{CrateGraph, CrateId, Edition, FileId}; |
15 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::FxHashMap; |
16 | use serde_json::from_reader; | 18 | use serde_json::from_reader; |
@@ -117,6 +119,7 @@ impl ProjectWorkspace { | |||
117 | 119 | ||
118 | pub fn to_crate_graph( | 120 | pub fn to_crate_graph( |
119 | &self, | 121 | &self, |
122 | default_cfg_options: &CfgOptions, | ||
120 | load: &mut dyn FnMut(&Path) -> Option<FileId>, | 123 | load: &mut dyn FnMut(&Path) -> Option<FileId>, |
121 | ) -> (CrateGraph, FxHashMap<CrateId, String>) { | 124 | ) -> (CrateGraph, FxHashMap<CrateId, String>) { |
122 | let mut crate_graph = CrateGraph::default(); | 125 | let mut crate_graph = CrateGraph::default(); |
@@ -131,7 +134,17 @@ impl ProjectWorkspace { | |||
131 | json_project::Edition::Edition2015 => Edition::Edition2015, | 134 | json_project::Edition::Edition2015 => Edition::Edition2015, |
132 | json_project::Edition::Edition2018 => Edition::Edition2018, | 135 | json_project::Edition::Edition2018 => Edition::Edition2018, |
133 | }; | 136 | }; |
134 | crates.insert(crate_id, crate_graph.add_crate_root(file_id, edition)); | 137 | let mut cfg_options = default_cfg_options.clone(); |
138 | for name in &krate.atom_cfgs { | ||
139 | cfg_options = cfg_options.atom(name.into()); | ||
140 | } | ||
141 | for (key, value) in &krate.key_value_cfgs { | ||
142 | cfg_options = cfg_options.key_value(key.into(), value.into()); | ||
143 | } | ||
144 | crates.insert( | ||
145 | crate_id, | ||
146 | crate_graph.add_crate_root(file_id, edition, cfg_options), | ||
147 | ); | ||
135 | } | 148 | } |
136 | } | 149 | } |
137 | 150 | ||
@@ -157,7 +170,10 @@ impl ProjectWorkspace { | |||
157 | let mut sysroot_crates = FxHashMap::default(); | 170 | let mut sysroot_crates = FxHashMap::default(); |
158 | for krate in sysroot.crates() { | 171 | for krate in sysroot.crates() { |
159 | if let Some(file_id) = load(krate.root(&sysroot)) { | 172 | if let Some(file_id) = load(krate.root(&sysroot)) { |
160 | let crate_id = crate_graph.add_crate_root(file_id, Edition::Edition2018); | 173 | // Crates from sysroot have `cfg(test)` disabled |
174 | let cfg_options = default_cfg_options.clone().remove_atom(&"test".into()); | ||
175 | let crate_id = | ||
176 | crate_graph.add_crate_root(file_id, Edition::Edition2018, cfg_options); | ||
161 | sysroot_crates.insert(krate, crate_id); | 177 | sysroot_crates.insert(krate, crate_id); |
162 | names.insert(crate_id, krate.name(&sysroot).to_string()); | 178 | names.insert(crate_id, krate.name(&sysroot).to_string()); |
163 | } | 179 | } |
@@ -186,7 +202,11 @@ impl ProjectWorkspace { | |||
186 | let root = tgt.root(&cargo); | 202 | let root = tgt.root(&cargo); |
187 | if let Some(file_id) = load(root) { | 203 | if let Some(file_id) = load(root) { |
188 | let edition = pkg.edition(&cargo); | 204 | let edition = pkg.edition(&cargo); |
189 | let crate_id = crate_graph.add_crate_root(file_id, edition); | 205 | let cfg_options = default_cfg_options |
206 | .clone() | ||
207 | .features(pkg.features(&cargo).iter().map(Into::into)); | ||
208 | let crate_id = | ||
209 | crate_graph.add_crate_root(file_id, edition, cfg_options); | ||
190 | names.insert(crate_id, pkg.name(&cargo).to_string()); | 210 | names.insert(crate_id, pkg.name(&cargo).to_string()); |
191 | if tgt.kind(&cargo) == TargetKind::Lib { | 211 | if tgt.kind(&cargo) == TargetKind::Lib { |
192 | lib_tgt = Some(crate_id); | 212 | lib_tgt = Some(crate_id); |
@@ -286,3 +306,32 @@ fn find_cargo_toml(path: &Path) -> Result<PathBuf> { | |||
286 | } | 306 | } |
287 | Err(format!("can't find Cargo.toml at {}", path.display()))? | 307 | Err(format!("can't find Cargo.toml at {}", path.display()))? |
288 | } | 308 | } |
309 | |||
310 | pub fn get_rustc_cfg_options() -> CfgOptions { | ||
311 | let mut cfg_options = CfgOptions::default(); | ||
312 | |||
313 | match (|| -> Result<_> { | ||
314 | // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. | ||
315 | let output = Command::new("rustc").args(&["--print", "cfg", "-O"]).output()?; | ||
316 | if !output.status.success() { | ||
317 | Err("failed to get rustc cfgs")?; | ||
318 | } | ||
319 | Ok(String::from_utf8(output.stdout)?) | ||
320 | })() { | ||
321 | Ok(rustc_cfgs) => { | ||
322 | for line in rustc_cfgs.lines() { | ||
323 | match line.find('=') { | ||
324 | None => cfg_options = cfg_options.atom(line.into()), | ||
325 | Some(pos) => { | ||
326 | let key = &line[..pos]; | ||
327 | let value = line[pos + 1..].trim_matches('"'); | ||
328 | cfg_options = cfg_options.key_value(key.into(), value.into()); | ||
329 | } | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | Err(e) => log::error!("failed to get rustc cfgs: {}", e), | ||
334 | } | ||
335 | |||
336 | cfg_options | ||
337 | } | ||
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index d55534ede..7cfea70f9 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -1,7 +1,5 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | pub mod visit; | ||
4 | |||
5 | use std::ops::RangeInclusive; | 3 | use std::ops::RangeInclusive; |
6 | 4 | ||
7 | use itertools::Itertools; | 5 | use itertools::Itertools; |
diff --git a/crates/ra_syntax/src/algo/visit.rs b/crates/ra_syntax/src/algo/visit.rs deleted file mode 100644 index 4df275ba4..000000000 --- a/crates/ra_syntax/src/algo/visit.rs +++ /dev/null | |||
@@ -1,112 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use crate::{AstNode, SyntaxNode}; | ||
4 | |||
5 | use std::marker::PhantomData; | ||
6 | |||
7 | pub fn visitor<'a, T>() -> impl Visitor<'a, Output = T> { | ||
8 | EmptyVisitor { ph: PhantomData } | ||
9 | } | ||
10 | |||
11 | pub fn visitor_ctx<'a, T, C>(ctx: C) -> impl VisitorCtx<'a, Output = T, Ctx = C> { | ||
12 | EmptyVisitorCtx { ph: PhantomData, ctx } | ||
13 | } | ||
14 | |||
15 | pub trait Visitor<'a>: Sized { | ||
16 | type Output; | ||
17 | fn accept(self, node: &'a SyntaxNode) -> Option<Self::Output>; | ||
18 | fn visit<N, F>(self, f: F) -> Vis<Self, N, F> | ||
19 | where | ||
20 | N: AstNode + 'a, | ||
21 | F: FnOnce(N) -> Self::Output, | ||
22 | { | ||
23 | Vis { inner: self, f, ph: PhantomData } | ||
24 | } | ||
25 | } | ||
26 | |||
27 | pub trait VisitorCtx<'a>: Sized { | ||
28 | type Output; | ||
29 | type Ctx; | ||
30 | fn accept(self, node: &'a SyntaxNode) -> Result<Self::Output, Self::Ctx>; | ||
31 | fn visit<N, F>(self, f: F) -> VisCtx<Self, N, F> | ||
32 | where | ||
33 | N: AstNode + 'a, | ||
34 | F: FnOnce(N, Self::Ctx) -> Self::Output, | ||
35 | { | ||
36 | VisCtx { inner: self, f, ph: PhantomData } | ||
37 | } | ||
38 | } | ||
39 | |||
40 | #[derive(Debug)] | ||
41 | struct EmptyVisitor<T> { | ||
42 | ph: PhantomData<fn() -> T>, | ||
43 | } | ||
44 | |||
45 | impl<'a, T> Visitor<'a> for EmptyVisitor<T> { | ||
46 | type Output = T; | ||
47 | |||
48 | fn accept(self, _node: &'a SyntaxNode) -> Option<T> { | ||
49 | None | ||
50 | } | ||
51 | } | ||
52 | |||
53 | #[derive(Debug)] | ||
54 | struct EmptyVisitorCtx<T, C> { | ||
55 | ctx: C, | ||
56 | ph: PhantomData<fn() -> T>, | ||
57 | } | ||
58 | |||
59 | impl<'a, T, C> VisitorCtx<'a> for EmptyVisitorCtx<T, C> { | ||
60 | type Output = T; | ||
61 | type Ctx = C; | ||
62 | |||
63 | fn accept(self, _node: &'a SyntaxNode) -> Result<T, C> { | ||
64 | Err(self.ctx) | ||
65 | } | ||
66 | } | ||
67 | |||
68 | #[derive(Debug)] | ||
69 | pub struct Vis<V, N, F> { | ||
70 | inner: V, | ||
71 | f: F, | ||
72 | ph: PhantomData<fn(N)>, | ||
73 | } | ||
74 | |||
75 | impl<'a, V, N, F> Visitor<'a> for Vis<V, N, F> | ||
76 | where | ||
77 | V: Visitor<'a>, | ||
78 | N: AstNode + 'a, | ||
79 | F: FnOnce(N) -> <V as Visitor<'a>>::Output, | ||
80 | { | ||
81 | type Output = <V as Visitor<'a>>::Output; | ||
82 | |||
83 | fn accept(self, node: &'a SyntaxNode) -> Option<Self::Output> { | ||
84 | let Vis { inner, f, .. } = self; | ||
85 | inner.accept(node).or_else(|| N::cast(node.clone()).map(f)) | ||
86 | } | ||
87 | } | ||
88 | |||
89 | #[derive(Debug)] | ||
90 | pub struct VisCtx<V, N, F> { | ||
91 | inner: V, | ||
92 | f: F, | ||
93 | ph: PhantomData<fn(N)>, | ||
94 | } | ||
95 | |||
96 | impl<'a, V, N, F> VisitorCtx<'a> for VisCtx<V, N, F> | ||
97 | where | ||
98 | V: VisitorCtx<'a>, | ||
99 | N: AstNode + 'a, | ||
100 | F: FnOnce(N, <V as VisitorCtx<'a>>::Ctx) -> <V as VisitorCtx<'a>>::Output, | ||
101 | { | ||
102 | type Output = <V as VisitorCtx<'a>>::Output; | ||
103 | type Ctx = <V as VisitorCtx<'a>>::Ctx; | ||
104 | |||
105 | fn accept(self, node: &'a SyntaxNode) -> Result<Self::Output, Self::Ctx> { | ||
106 | let VisCtx { inner, f, .. } = self; | ||
107 | inner.accept(node).or_else(|ctx| match N::cast(node.clone()) { | ||
108 | None => Err(ctx), | ||
109 | Some(node) => Ok(f(node, ctx)), | ||
110 | }) | ||
111 | } | ||
112 | } | ||
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 3bb5571ee..34b22c3e2 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -1962,6 +1962,7 @@ impl AstNode for ModuleItem { | |||
1962 | } | 1962 | } |
1963 | } | 1963 | } |
1964 | } | 1964 | } |
1965 | impl ast::AttrsOwner for ModuleItem {} | ||
1965 | impl ModuleItem {} | 1966 | impl ModuleItem {} |
1966 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 1967 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
1967 | pub struct Name { | 1968 | pub struct Name { |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 30328f59f..25e6f64ce 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -1,5 +1,5 @@ | |||
1 | // Stores definitions which must be used in multiple places | 1 | // Stores definitions which must be used in multiple places |
2 | // See `cargo gen-syntax` (defined in crates/tools/src/main.rs) | 2 | // See `cargo gen-syntax` (defined in crates/ra_tools/src/main.rs) |
3 | Grammar( | 3 | Grammar( |
4 | punct: [ | 4 | punct: [ |
5 | (";", "SEMI"), | 5 | (";", "SEMI"), |
@@ -397,7 +397,8 @@ Grammar( | |||
397 | ), | 397 | ), |
398 | "ModuleItem": ( | 398 | "ModuleItem": ( |
399 | enum: ["StructDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock", | 399 | enum: ["StructDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock", |
400 | "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ] | 400 | "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ], |
401 | traits: ["AttrsOwner"] | ||
401 | ), | 402 | ), |
402 | "ImplItem": ( | 403 | "ImplItem": ( |
403 | enum: ["FnDef", "TypeAliasDef", "ConstDef"] | 404 | enum: ["FnDef", "TypeAliasDef", "ConstDef"] |
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index edb6076bb..c315ba552 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs | |||
@@ -160,6 +160,17 @@ impl SourceFile { | |||
160 | } | 160 | } |
161 | } | 161 | } |
162 | 162 | ||
163 | #[macro_export] | ||
164 | macro_rules! match_ast { | ||
165 | (match $node:ident { | ||
166 | $( ast::$ast:ident($it:ident) => $res:block, )* | ||
167 | _ => $catch_all:expr, | ||
168 | }) => {{ | ||
169 | $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* | ||
170 | { $catch_all } | ||
171 | }}; | ||
172 | } | ||
173 | |||
163 | /// This test does not assert anything and instead just shows off the crate's | 174 | /// This test does not assert anything and instead just shows off the crate's |
164 | /// API. | 175 | /// API. |
165 | #[test] | 176 | #[test] |
@@ -294,7 +305,7 @@ fn api_walkthrough() { | |||
294 | // To recursively process the tree, there are three approaches: | 305 | // To recursively process the tree, there are three approaches: |
295 | // 1. explicitly call getter methods on AST nodes. | 306 | // 1. explicitly call getter methods on AST nodes. |
296 | // 2. use descendants and `AstNode::cast`. | 307 | // 2. use descendants and `AstNode::cast`. |
297 | // 3. use descendants and the visitor. | 308 | // 3. use descendants and `match_ast!`. |
298 | // | 309 | // |
299 | // Here's how the first one looks like: | 310 | // Here's how the first one looks like: |
300 | let exprs_cast: Vec<String> = file | 311 | let exprs_cast: Vec<String> = file |
@@ -304,17 +315,17 @@ fn api_walkthrough() { | |||
304 | .map(|expr| expr.syntax().text().to_string()) | 315 | .map(|expr| expr.syntax().text().to_string()) |
305 | .collect(); | 316 | .collect(); |
306 | 317 | ||
307 | // An alternative is to use a visitor. The visitor does not do traversal | 318 | // An alternative is to use a macro. |
308 | // automatically (so it's more akin to a generic lambda) and is constructed | ||
309 | // from closures. This seems more flexible than a single generated visitor | ||
310 | // trait. | ||
311 | use algo::visit::{visitor, Visitor}; | ||
312 | let mut exprs_visit = Vec::new(); | 319 | let mut exprs_visit = Vec::new(); |
313 | for node in file.syntax().descendants() { | 320 | for node in file.syntax().descendants() { |
314 | if let Some(result) = | 321 | match_ast! { |
315 | visitor().visit::<ast::Expr, _>(|expr| expr.syntax().text().to_string()).accept(&node) | 322 | match node { |
316 | { | 323 | ast::Expr(it) => { |
317 | exprs_visit.push(result); | 324 | let res = it.syntax().text().to_string(); |
325 | exprs_visit.push(res); | ||
326 | }, | ||
327 | _ => (), | ||
328 | } | ||
318 | } | 329 | } |
319 | } | 330 | } |
320 | assert_eq!(exprs_cast, exprs_visit); | 331 | assert_eq!(exprs_cast, exprs_visit); |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 4f8935b2c..ab4f15908 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -5,8 +5,7 @@ mod block; | |||
5 | use rustc_lexer::unescape; | 5 | use rustc_lexer::unescape; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | algo::visit::{visitor_ctx, VisitorCtx}, | 8 | ast, match_ast, AstNode, SyntaxError, SyntaxErrorKind, |
9 | ast, AstNode, SyntaxError, SyntaxErrorKind, | ||
10 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, INT_NUMBER, STRING}, | 9 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, INT_NUMBER, STRING}, |
11 | SyntaxNode, SyntaxToken, TextUnit, T, | 10 | SyntaxNode, SyntaxToken, TextUnit, T, |
12 | }; | 11 | }; |
@@ -97,12 +96,15 @@ impl From<rustc_lexer::unescape::EscapeError> for SyntaxErrorKind { | |||
97 | pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | 96 | pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { |
98 | let mut errors = Vec::new(); | 97 | let mut errors = Vec::new(); |
99 | for node in root.descendants() { | 98 | for node in root.descendants() { |
100 | let _ = visitor_ctx(&mut errors) | 99 | match_ast! { |
101 | .visit::<ast::Literal, _>(validate_literal) | 100 | match node { |
102 | .visit::<ast::BlockExpr, _>(block::validate_block_expr) | 101 | ast::Literal(it) => { validate_literal(it, &mut errors) }, |
103 | .visit::<ast::FieldExpr, _>(|it, errors| validate_numeric_name(it.name_ref(), errors)) | 102 | ast::BlockExpr(it) => { block::validate_block_expr(it, &mut errors) }, |
104 | .visit::<ast::RecordField, _>(|it, errors| validate_numeric_name(it.name_ref(), errors)) | 103 | ast::FieldExpr(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, |
105 | .accept(&node); | 104 | ast::RecordField(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, |
105 | _ => (), | ||
106 | } | ||
107 | } | ||
106 | } | 108 | } |
107 | errors | 109 | errors |
108 | } | 110 | } |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.rs b/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.rs index 293046a04..2f8188160 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | fn foo() { | 1 | fn foo() { |
2 | while true {}; | 2 | while true {}; |
3 | while let Some(x) = it.next() {}; | 3 | while let Some(x) = it.next() {}; |
4 | while { true } {}; | ||
4 | } | 5 | } |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.txt b/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.txt index 9b43d624c..a6e14a114 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.txt +++ b/crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.txt | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@[0; 70) | 1 | SOURCE_FILE@[0; 93) |
2 | FN_DEF@[0; 69) | 2 | FN_DEF@[0; 92) |
3 | FN_KW@[0; 2) "fn" | 3 | FN_KW@[0; 2) "fn" |
4 | WHITESPACE@[2; 3) " " | 4 | WHITESPACE@[2; 3) " " |
5 | NAME@[3; 6) | 5 | NAME@[3; 6) |
@@ -8,8 +8,8 @@ SOURCE_FILE@[0; 70) | |||
8 | L_PAREN@[6; 7) "(" | 8 | L_PAREN@[6; 7) "(" |
9 | R_PAREN@[7; 8) ")" | 9 | R_PAREN@[7; 8) ")" |
10 | WHITESPACE@[8; 9) " " | 10 | WHITESPACE@[8; 9) " " |
11 | BLOCK_EXPR@[9; 69) | 11 | BLOCK_EXPR@[9; 92) |
12 | BLOCK@[9; 69) | 12 | BLOCK@[9; 92) |
13 | L_CURLY@[9; 10) "{" | 13 | L_CURLY@[9; 10) "{" |
14 | WHITESPACE@[10; 15) "\n " | 14 | WHITESPACE@[10; 15) "\n " |
15 | EXPR_STMT@[15; 29) | 15 | EXPR_STMT@[15; 29) |
@@ -64,6 +64,26 @@ SOURCE_FILE@[0; 70) | |||
64 | L_CURLY@[64; 65) "{" | 64 | L_CURLY@[64; 65) "{" |
65 | R_CURLY@[65; 66) "}" | 65 | R_CURLY@[65; 66) "}" |
66 | SEMI@[66; 67) ";" | 66 | SEMI@[66; 67) ";" |
67 | WHITESPACE@[67; 68) "\n" | 67 | WHITESPACE@[67; 72) "\n " |
68 | R_CURLY@[68; 69) "}" | 68 | EXPR_STMT@[72; 90) |
69 | WHITESPACE@[69; 70) "\n" | 69 | WHILE_EXPR@[72; 89) |
70 | WHILE_KW@[72; 77) "while" | ||
71 | WHITESPACE@[77; 78) " " | ||
72 | CONDITION@[78; 86) | ||
73 | BLOCK_EXPR@[78; 86) | ||
74 | BLOCK@[78; 86) | ||
75 | L_CURLY@[78; 79) "{" | ||
76 | WHITESPACE@[79; 80) " " | ||
77 | LITERAL@[80; 84) | ||
78 | TRUE_KW@[80; 84) "true" | ||
79 | WHITESPACE@[84; 85) " " | ||
80 | R_CURLY@[85; 86) "}" | ||
81 | WHITESPACE@[86; 87) " " | ||
82 | BLOCK_EXPR@[87; 89) | ||
83 | BLOCK@[87; 89) | ||
84 | L_CURLY@[87; 88) "{" | ||
85 | R_CURLY@[88; 89) "}" | ||
86 | SEMI@[89; 90) ";" | ||
87 | WHITESPACE@[90; 91) "\n" | ||
88 | R_CURLY@[91; 92) "}" | ||
89 | WHITESPACE@[92; 93) "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.rs b/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.rs index 4b0d9af89..40f227ba3 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.rs | |||
@@ -3,4 +3,5 @@ fn foo() { | |||
3 | if true {} else {}; | 3 | if true {} else {}; |
4 | if true {} else if false {} else {}; | 4 | if true {} else if false {} else {}; |
5 | if S {}; | 5 | if S {}; |
6 | if { true } { } else { }; | ||
6 | } | 7 | } |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.txt b/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.txt index 14ea91fd2..2ace3c8ee 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.txt +++ b/crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.txt | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@[0; 107) | 1 | SOURCE_FILE@[0; 137) |
2 | FN_DEF@[0; 106) | 2 | FN_DEF@[0; 136) |
3 | FN_KW@[0; 2) "fn" | 3 | FN_KW@[0; 2) "fn" |
4 | WHITESPACE@[2; 3) " " | 4 | WHITESPACE@[2; 3) " " |
5 | NAME@[3; 6) | 5 | NAME@[3; 6) |
@@ -8,8 +8,8 @@ SOURCE_FILE@[0; 107) | |||
8 | L_PAREN@[6; 7) "(" | 8 | L_PAREN@[6; 7) "(" |
9 | R_PAREN@[7; 8) ")" | 9 | R_PAREN@[7; 8) ")" |
10 | WHITESPACE@[8; 9) " " | 10 | WHITESPACE@[8; 9) " " |
11 | BLOCK_EXPR@[9; 106) | 11 | BLOCK_EXPR@[9; 136) |
12 | BLOCK@[9; 106) | 12 | BLOCK@[9; 136) |
13 | L_CURLY@[9; 10) "{" | 13 | L_CURLY@[9; 10) "{" |
14 | WHITESPACE@[10; 15) "\n " | 14 | WHITESPACE@[10; 15) "\n " |
15 | EXPR_STMT@[15; 26) | 15 | EXPR_STMT@[15; 26) |
@@ -98,6 +98,35 @@ SOURCE_FILE@[0; 107) | |||
98 | L_CURLY@[101; 102) "{" | 98 | L_CURLY@[101; 102) "{" |
99 | R_CURLY@[102; 103) "}" | 99 | R_CURLY@[102; 103) "}" |
100 | SEMI@[103; 104) ";" | 100 | SEMI@[103; 104) ";" |
101 | WHITESPACE@[104; 105) "\n" | 101 | WHITESPACE@[104; 109) "\n " |
102 | R_CURLY@[105; 106) "}" | 102 | EXPR_STMT@[109; 134) |
103 | WHITESPACE@[106; 107) "\n" | 103 | IF_EXPR@[109; 133) |
104 | IF_KW@[109; 111) "if" | ||
105 | WHITESPACE@[111; 112) " " | ||
106 | CONDITION@[112; 120) | ||
107 | BLOCK_EXPR@[112; 120) | ||
108 | BLOCK@[112; 120) | ||
109 | L_CURLY@[112; 113) "{" | ||
110 | WHITESPACE@[113; 114) " " | ||
111 | LITERAL@[114; 118) | ||
112 | TRUE_KW@[114; 118) "true" | ||
113 | WHITESPACE@[118; 119) " " | ||
114 | R_CURLY@[119; 120) "}" | ||
115 | WHITESPACE@[120; 121) " " | ||
116 | BLOCK_EXPR@[121; 124) | ||
117 | BLOCK@[121; 124) | ||
118 | L_CURLY@[121; 122) "{" | ||
119 | WHITESPACE@[122; 123) " " | ||
120 | R_CURLY@[123; 124) "}" | ||
121 | WHITESPACE@[124; 125) " " | ||
122 | ELSE_KW@[125; 129) "else" | ||
123 | WHITESPACE@[129; 130) " " | ||
124 | BLOCK_EXPR@[130; 133) | ||
125 | BLOCK@[130; 133) | ||
126 | L_CURLY@[130; 131) "{" | ||
127 | WHITESPACE@[131; 132) " " | ||
128 | R_CURLY@[132; 133) "}" | ||
129 | SEMI@[133; 134) ";" | ||
130 | WHITESPACE@[134; 135) "\n" | ||
131 | R_CURLY@[135; 136) "}" | ||
132 | WHITESPACE@[136; 137) "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.rs b/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.rs index c9205dfa3..c4021dc10 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.rs | |||
@@ -1,4 +1,6 @@ | |||
1 | fn foo() { | 1 | fn foo() { |
2 | match () { }; | 2 | match () { }; |
3 | match S {}; | 3 | match S {}; |
4 | match { } { _ => () }; | ||
5 | match { S {} } {}; | ||
4 | } | 6 | } |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.txt b/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.txt index f62b6c6d5..0af668056 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.txt +++ b/crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.txt | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@[0; 47) | 1 | SOURCE_FILE@[0; 97) |
2 | FN_DEF@[0; 46) | 2 | FN_DEF@[0; 96) |
3 | FN_KW@[0; 2) "fn" | 3 | FN_KW@[0; 2) "fn" |
4 | WHITESPACE@[2; 3) " " | 4 | WHITESPACE@[2; 3) " " |
5 | NAME@[3; 6) | 5 | NAME@[3; 6) |
@@ -8,8 +8,8 @@ SOURCE_FILE@[0; 47) | |||
8 | L_PAREN@[6; 7) "(" | 8 | L_PAREN@[6; 7) "(" |
9 | R_PAREN@[7; 8) ")" | 9 | R_PAREN@[7; 8) ")" |
10 | WHITESPACE@[8; 9) " " | 10 | WHITESPACE@[8; 9) " " |
11 | BLOCK_EXPR@[9; 46) | 11 | BLOCK_EXPR@[9; 96) |
12 | BLOCK@[9; 46) | 12 | BLOCK@[9; 96) |
13 | L_CURLY@[9; 10) "{" | 13 | L_CURLY@[9; 10) "{" |
14 | WHITESPACE@[10; 15) "\n " | 14 | WHITESPACE@[10; 15) "\n " |
15 | EXPR_STMT@[15; 28) | 15 | EXPR_STMT@[15; 28) |
@@ -40,6 +40,57 @@ SOURCE_FILE@[0; 47) | |||
40 | L_CURLY@[41; 42) "{" | 40 | L_CURLY@[41; 42) "{" |
41 | R_CURLY@[42; 43) "}" | 41 | R_CURLY@[42; 43) "}" |
42 | SEMI@[43; 44) ";" | 42 | SEMI@[43; 44) ";" |
43 | WHITESPACE@[44; 45) "\n" | 43 | WHITESPACE@[44; 49) "\n " |
44 | R_CURLY@[45; 46) "}" | 44 | EXPR_STMT@[49; 71) |
45 | WHITESPACE@[46; 47) "\n" | 45 | MATCH_EXPR@[49; 70) |
46 | MATCH_KW@[49; 54) "match" | ||
47 | WHITESPACE@[54; 55) " " | ||
48 | BLOCK_EXPR@[55; 58) | ||
49 | BLOCK@[55; 58) | ||
50 | L_CURLY@[55; 56) "{" | ||
51 | WHITESPACE@[56; 57) " " | ||
52 | R_CURLY@[57; 58) "}" | ||
53 | WHITESPACE@[58; 59) " " | ||
54 | MATCH_ARM_LIST@[59; 70) | ||
55 | L_CURLY@[59; 60) "{" | ||
56 | WHITESPACE@[60; 61) " " | ||
57 | MATCH_ARM@[61; 68) | ||
58 | PLACEHOLDER_PAT@[61; 62) | ||
59 | UNDERSCORE@[61; 62) "_" | ||
60 | WHITESPACE@[62; 63) " " | ||
61 | FAT_ARROW@[63; 65) "=>" | ||
62 | WHITESPACE@[65; 66) " " | ||
63 | TUPLE_EXPR@[66; 68) | ||
64 | L_PAREN@[66; 67) "(" | ||
65 | R_PAREN@[67; 68) ")" | ||
66 | WHITESPACE@[68; 69) " " | ||
67 | R_CURLY@[69; 70) "}" | ||
68 | SEMI@[70; 71) ";" | ||
69 | WHITESPACE@[71; 76) "\n " | ||
70 | EXPR_STMT@[76; 94) | ||
71 | MATCH_EXPR@[76; 93) | ||
72 | MATCH_KW@[76; 81) "match" | ||
73 | WHITESPACE@[81; 82) " " | ||
74 | BLOCK_EXPR@[82; 90) | ||
75 | BLOCK@[82; 90) | ||
76 | L_CURLY@[82; 83) "{" | ||
77 | WHITESPACE@[83; 84) " " | ||
78 | RECORD_LIT@[84; 88) | ||
79 | PATH@[84; 85) | ||
80 | PATH_SEGMENT@[84; 85) | ||
81 | NAME_REF@[84; 85) | ||
82 | IDENT@[84; 85) "S" | ||
83 | WHITESPACE@[85; 86) " " | ||
84 | RECORD_FIELD_LIST@[86; 88) | ||
85 | L_CURLY@[86; 87) "{" | ||
86 | R_CURLY@[87; 88) "}" | ||
87 | WHITESPACE@[88; 89) " " | ||
88 | R_CURLY@[89; 90) "}" | ||
89 | WHITESPACE@[90; 91) " " | ||
90 | MATCH_ARM_LIST@[91; 93) | ||
91 | L_CURLY@[91; 92) "{" | ||
92 | R_CURLY@[92; 93) "}" | ||
93 | SEMI@[93; 94) ";" | ||
94 | WHITESPACE@[94; 95) "\n" | ||
95 | R_CURLY@[95; 96) "}" | ||
96 | WHITESPACE@[96; 97) "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.rs b/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.rs index c39fe8e68..e7b7cfc6b 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.rs | |||
@@ -1 +1,5 @@ | |||
1 | fn foo() { let x = 1..; } | 1 | fn foo() { |
2 | let x = 1..; | ||
3 | match 1.. { _ => () }; | ||
4 | match a.b()..S { _ => () }; | ||
5 | } | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.txt b/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.txt index f3c292f5e..9f8a6b0f6 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.txt +++ b/crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.txt | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@[0; 26) | 1 | SOURCE_FILE@[0; 89) |
2 | FN_DEF@[0; 25) | 2 | FN_DEF@[0; 88) |
3 | FN_KW@[0; 2) "fn" | 3 | FN_KW@[0; 2) "fn" |
4 | WHITESPACE@[2; 3) " " | 4 | WHITESPACE@[2; 3) " " |
5 | NAME@[3; 6) | 5 | NAME@[3; 6) |
@@ -8,24 +8,89 @@ SOURCE_FILE@[0; 26) | |||
8 | L_PAREN@[6; 7) "(" | 8 | L_PAREN@[6; 7) "(" |
9 | R_PAREN@[7; 8) ")" | 9 | R_PAREN@[7; 8) ")" |
10 | WHITESPACE@[8; 9) " " | 10 | WHITESPACE@[8; 9) " " |
11 | BLOCK_EXPR@[9; 25) | 11 | BLOCK_EXPR@[9; 88) |
12 | BLOCK@[9; 25) | 12 | BLOCK@[9; 88) |
13 | L_CURLY@[9; 10) "{" | 13 | L_CURLY@[9; 10) "{" |
14 | WHITESPACE@[10; 11) " " | 14 | WHITESPACE@[10; 15) "\n " |
15 | LET_STMT@[11; 23) | 15 | LET_STMT@[15; 27) |
16 | LET_KW@[11; 14) "let" | 16 | LET_KW@[15; 18) "let" |
17 | WHITESPACE@[14; 15) " " | ||
18 | BIND_PAT@[15; 16) | ||
19 | NAME@[15; 16) | ||
20 | IDENT@[15; 16) "x" | ||
21 | WHITESPACE@[16; 17) " " | ||
22 | EQ@[17; 18) "=" | ||
23 | WHITESPACE@[18; 19) " " | 17 | WHITESPACE@[18; 19) " " |
24 | RANGE_EXPR@[19; 22) | 18 | BIND_PAT@[19; 20) |
25 | LITERAL@[19; 20) | 19 | NAME@[19; 20) |
26 | INT_NUMBER@[19; 20) "1" | 20 | IDENT@[19; 20) "x" |
27 | DOTDOT@[20; 22) ".." | 21 | WHITESPACE@[20; 21) " " |
28 | SEMI@[22; 23) ";" | 22 | EQ@[21; 22) "=" |
29 | WHITESPACE@[23; 24) " " | 23 | WHITESPACE@[22; 23) " " |
30 | R_CURLY@[24; 25) "}" | 24 | RANGE_EXPR@[23; 26) |
31 | WHITESPACE@[25; 26) "\n" | 25 | LITERAL@[23; 24) |
26 | INT_NUMBER@[23; 24) "1" | ||
27 | DOTDOT@[24; 26) ".." | ||
28 | SEMI@[26; 27) ";" | ||
29 | WHITESPACE@[27; 32) "\n " | ||
30 | EXPR_STMT@[32; 54) | ||
31 | MATCH_EXPR@[32; 53) | ||
32 | MATCH_KW@[32; 37) "match" | ||
33 | WHITESPACE@[37; 38) " " | ||
34 | RANGE_EXPR@[38; 41) | ||
35 | LITERAL@[38; 39) | ||
36 | INT_NUMBER@[38; 39) "1" | ||
37 | DOTDOT@[39; 41) ".." | ||
38 | WHITESPACE@[41; 42) " " | ||
39 | MATCH_ARM_LIST@[42; 53) | ||
40 | L_CURLY@[42; 43) "{" | ||
41 | WHITESPACE@[43; 44) " " | ||
42 | MATCH_ARM@[44; 51) | ||
43 | PLACEHOLDER_PAT@[44; 45) | ||
44 | UNDERSCORE@[44; 45) "_" | ||
45 | WHITESPACE@[45; 46) " " | ||
46 | FAT_ARROW@[46; 48) "=>" | ||
47 | WHITESPACE@[48; 49) " " | ||
48 | TUPLE_EXPR@[49; 51) | ||
49 | L_PAREN@[49; 50) "(" | ||
50 | R_PAREN@[50; 51) ")" | ||
51 | WHITESPACE@[51; 52) " " | ||
52 | R_CURLY@[52; 53) "}" | ||
53 | SEMI@[53; 54) ";" | ||
54 | WHITESPACE@[54; 59) "\n " | ||
55 | EXPR_STMT@[59; 86) | ||
56 | MATCH_EXPR@[59; 85) | ||
57 | MATCH_KW@[59; 64) "match" | ||
58 | WHITESPACE@[64; 65) " " | ||
59 | RANGE_EXPR@[65; 73) | ||
60 | METHOD_CALL_EXPR@[65; 70) | ||
61 | PATH_EXPR@[65; 66) | ||
62 | PATH@[65; 66) | ||
63 | PATH_SEGMENT@[65; 66) | ||
64 | NAME_REF@[65; 66) | ||
65 | IDENT@[65; 66) "a" | ||
66 | DOT@[66; 67) "." | ||
67 | NAME_REF@[67; 68) | ||
68 | IDENT@[67; 68) "b" | ||
69 | ARG_LIST@[68; 70) | ||
70 | L_PAREN@[68; 69) "(" | ||
71 | R_PAREN@[69; 70) ")" | ||
72 | DOTDOT@[70; 72) ".." | ||
73 | PATH_EXPR@[72; 73) | ||
74 | PATH@[72; 73) | ||
75 | PATH_SEGMENT@[72; 73) | ||
76 | NAME_REF@[72; 73) | ||
77 | IDENT@[72; 73) "S" | ||
78 | WHITESPACE@[73; 74) " " | ||
79 | MATCH_ARM_LIST@[74; 85) | ||
80 | L_CURLY@[74; 75) "{" | ||
81 | WHITESPACE@[75; 76) " " | ||
82 | MATCH_ARM@[76; 83) | ||
83 | PLACEHOLDER_PAT@[76; 77) | ||
84 | UNDERSCORE@[76; 77) "_" | ||
85 | WHITESPACE@[77; 78) " " | ||
86 | FAT_ARROW@[78; 80) "=>" | ||
87 | WHITESPACE@[80; 81) " " | ||
88 | TUPLE_EXPR@[81; 83) | ||
89 | L_PAREN@[81; 82) "(" | ||
90 | R_PAREN@[82; 83) ")" | ||
91 | WHITESPACE@[83; 84) " " | ||
92 | R_CURLY@[84; 85) "}" | ||
93 | SEMI@[85; 86) ";" | ||
94 | WHITESPACE@[86; 87) "\n" | ||
95 | R_CURLY@[87; 88) "}" | ||
96 | WHITESPACE@[88; 89) "\n" | ||