aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/assists/apply_demorgan.rs102
-rw-r--r--crates/ra_assists/src/assists/split_import.rs6
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_batch/src/lib.rs18
-rw-r--r--crates/ra_cfg/Cargo.toml14
-rw-r--r--crates/ra_cfg/src/cfg_expr.rs132
-rw-r--r--crates/ra_cfg/src/lib.rs61
-rw-r--r--crates/ra_db/Cargo.toml1
-rw-r--r--crates/ra_db/src/input.rs33
-rw-r--r--crates/ra_hir/Cargo.toml1
-rw-r--r--crates/ra_hir/src/attr.rs80
-rw-r--r--crates/ra_hir/src/impl_block.rs34
-rw-r--r--crates/ra_hir/src/lib.rs1
-rw-r--r--crates/ra_hir/src/mock.rs23
-rw-r--r--crates/ra_hir/src/nameres/collector.rs53
-rw-r--r--crates/ra_hir/src/nameres/raw.rs54
-rw-r--r--crates/ra_hir/src/nameres/tests.rs70
-rw-r--r--crates/ra_hir/src/ty/tests.rs45
-rw-r--r--crates/ra_ide_api/Cargo.toml1
-rw-r--r--crates/ra_ide_api/src/completion/complete_fn_param.rs16
-rw-r--r--crates/ra_ide_api/src/completion/complete_keyword.rs18
-rw-r--r--crates/ra_ide_api/src/display/navigation_target.rs59
-rw-r--r--crates/ra_ide_api/src/display/structure.rs116
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs185
-rw-r--r--crates/ra_ide_api/src/hover.rs77
-rw-r--r--crates/ra_ide_api/src/inlay_hints.rs104
-rw-r--r--crates/ra_ide_api/src/lib.rs6
-rw-r--r--crates/ra_ide_api/src/mock_analysis.rs6
-rw-r--r--crates/ra_ide_api/src/parent_module.rs3
-rw-r--r--crates/ra_ide_api/src/symbol_index.rs26
-rw-r--r--crates/ra_lsp_server/Cargo.toml1
-rw-r--r--crates/ra_lsp_server/src/world.rs8
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/main.rs8
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs25
-rw-r--r--crates/ra_parser/src/grammar/expressions/atom.rs10
-rw-r--r--crates/ra_project_model/Cargo.toml1
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs6
-rw-r--r--crates/ra_project_model/src/json_project.rs3
-rw-r--r--crates/ra_project_model/src/lib.rs55
-rw-r--r--crates/ra_syntax/src/algo.rs2
-rw-r--r--crates/ra_syntax/src/algo/visit.rs112
-rw-r--r--crates/ra_syntax/src/ast/generated.rs1
-rw-r--r--crates/ra_syntax/src/grammar.ron5
-rw-r--r--crates/ra_syntax/src/lib.rs31
-rw-r--r--crates/ra_syntax/src/validation.rs18
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0031_while_expr.txt34
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0064_if_expr.txt43
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.rs2
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0071_match_expr.txt65
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.rs6
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0080_postfix_range.txt107
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)`.
4use hir::db::HirDatabase;
5use ra_syntax::ast::{self, AstNode};
6use ra_syntax::SyntaxNode;
7
8use 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`.
16pub(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
42fn 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
51fn 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)]
73mod 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;
7use crossbeam_channel::{unbounded, Receiver}; 7use crossbeam_channel::{unbounded, Receiver};
8use ra_db::{CrateGraph, FileId, SourceRootId}; 8use ra_db::{CrateGraph, FileId, SourceRootId};
9use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags}; 9use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags};
10use ra_project_model::{PackageRoot, ProjectWorkspace}; 10use ra_project_model::{get_rustc_cfg_options, PackageRoot, ProjectWorkspace};
11use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; 11use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
12use ra_vfs_glob::RustPackageFilterBuilder; 12use 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]
2edition = "2018"
3name = "ra_cfg"
4version = "0.1.0"
5authors = ["rust-analyzer developers"]
6
7[dependencies]
8rustc-hash = "1.0.1"
9
10ra_syntax = { path = "../ra_syntax" }
11tt = { path = "../ra_tt", package = "ra_tt" }
12
13[dev-dependencies]
14mbe = { 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
5use std::slice::Iter as SliceIter;
6
7use ra_syntax::SmolStr;
8use tt::{Leaf, Subtree, TokenTree};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub 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
20impl 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
38pub fn parse_cfg(tt: &Subtree) -> CfgExpr {
39 next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid)
40}
41
42fn 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)]
88mod 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
2use std::iter::IntoIterator;
3
4use ra_syntax::SmolStr;
5use rustc_hash::FxHashSet;
6
7mod cfg_expr;
8
9pub 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)]
22pub struct CfgOptions {
23 atoms: FxHashSet<SmolStr>,
24 key_values: FxHashSet<(SmolStr, SmolStr)>,
25}
26
27impl 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"
10rustc-hash = "1.0" 10rustc-hash = "1.0"
11 11
12ra_syntax = { path = "../ra_syntax" } 12ra_syntax = { path = "../ra_syntax" }
13ra_cfg = { path = "../ra_cfg" }
13ra_prof = { path = "../ra_prof" } 14ra_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 @@
9use relative_path::{RelativePath, RelativePathBuf}; 9use relative_path::{RelativePath, RelativePathBuf};
10use rustc_hash::FxHashMap; 10use rustc_hash::FxHashMap;
11 11
12use ra_cfg::CfgOptions;
12use ra_syntax::SmolStr; 13use ra_syntax::SmolStr;
13use rustc_hash::FxHashSet; 14use 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
114impl CrateData { 116impl 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
136impl CrateGraph { 138impl 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)]
223mod tests { 234mod 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
16ra_syntax = { path = "../ra_syntax" } 16ra_syntax = { path = "../ra_syntax" }
17ra_arena = { path = "../ra_arena" } 17ra_arena = { path = "../ra_arena" }
18ra_cfg = { path = "../ra_cfg" }
18ra_db = { path = "../ra_db" } 19ra_db = { path = "../ra_db" }
19mbe = { path = "../ra_mbe", package = "ra_mbe" } 20mbe = { path = "../ra_mbe", package = "ra_mbe" }
20tt = { path = "../ra_tt", package = "ra_tt" } 21tt = { 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
3use std::sync::Arc;
4
5use mbe::ast_to_token_tree;
6use ra_cfg::CfgOptions;
7use ra_syntax::{
8 ast::{self, AstNode, AttrsOwner},
9 SmolStr,
10};
11use tt::Subtree;
12
13use crate::{db::AstDatabase, path::Path, HirFileId, Source};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub(crate) struct Attr {
17 pub(crate) path: Path,
18 pub(crate) input: Option<AttrInput>,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum AttrInput {
23 Literal(SmolStr),
24 TokenTree(Subtree),
25}
26
27impl 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;
4use std::sync::Arc; 4use std::sync::Arc;
5 5
6use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; 6use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
7use ra_cfg::CfgOptions;
7use ra_syntax::{ 8use ra_syntax::{
8 ast::{self, AstNode}, 9 ast::{self, AstNode},
9 AstPtr, 10 AstPtr,
10}; 11};
11 12
12use crate::{ 13use 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 {
176impl ModuleImplBlocks { 178impl 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, &macro_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(&macro_call).with_file_id(file_id); 240 let ast_id = db.ast_id_map(file_id).ast_id(&macro_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;
44mod type_alias; 44mod type_alias;
45mod type_ref; 45mod type_ref;
46mod ty; 46mod ty;
47mod attr;
47mod impl_block; 48mod impl_block;
48mod expr; 49mod expr;
49mod lang_item; 50mod 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 @@
3use std::{panic, sync::Arc}; 3use std::{panic, sync::Arc};
4 4
5use parking_lot::Mutex; 5use parking_lot::Mutex;
6use ra_cfg::CfgOptions;
6use ra_db::{ 7use 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)]
271pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, Vec<String>))>); 272pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, CfgOptions, Vec<String>))>);
272 273
273#[macro_export] 274#[macro_export]
274macro_rules! crate_graph { 275macro_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
3use ra_cfg::CfgOptions;
3use ra_db::FileId; 4use ra_db::FileId;
4use ra_syntax::{ast, SmolStr}; 5use ra_syntax::{ast, SmolStr};
5use rustc_hash::FxHashMap; 6use 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
79struct DefCollector<DB> { 84struct 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
91impl<'a, DB> DefCollector<&'a DB> 98impl<DB> DefCollector<'_, DB>
92where 99where
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
509impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> 516impl<DB> ModCollector<'_, &'_ mut DefCollector<'_, DB>>
510where 517where
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
708fn is_macro_rules(path: &Path) -> bool { 726fn 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::{
10use test_utils::tested_by; 10use test_utils::tested_by;
11 11
12use crate::{ 12use 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.
124pub(super) type Attrs = Option<Arc<[Attr]>>;
125
126#[derive(Debug, PartialEq, Eq, Clone)]
127pub(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)]
123pub(super) enum RawItem { 133pub(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;
7use std::sync::Arc; 7use std::sync::Arc;
8 8
9use insta::assert_snapshot; 9use insta::assert_snapshot;
10use ra_cfg::CfgOptions;
10use ra_db::SourceDatabase; 11use ra_db::SourceDatabase;
11use test_utils::covers; 12use 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]
513fn 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]
545fn 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
4use insta::assert_snapshot; 4use insta::assert_snapshot;
5 5
6use ra_cfg::CfgOptions;
6use ra_db::{salsa::Database, FilePosition, SourceDatabase}; 7use ra_db::{salsa::Database, FilePosition, SourceDatabase};
7use ra_syntax::{ 8use ra_syntax::{
8 algo, 9 algo,
@@ -24,6 +25,50 @@ mod never_type;
24mod coercion; 25mod coercion;
25 26
26#[test] 27#[test]
28fn cfg_impl_block() {
29 let (mut db, pos) = MockDatabase::with_position(
30 r#"
31//- /main.rs
32use foo::S as T;
33struct S;
34
35#[cfg(test)]
36impl S {
37 fn foo1(&self) -> i32 { 0 }
38}
39
40#[cfg(not(test))]
41impl S {
42 fn foo2(&self) -> i32 { 0 }
43}
44
45fn test() {
46 let t = (S.foo1(), S.foo2(), T.foo3(), T.foo4());
47 t<|>;
48}
49
50//- /foo.rs
51struct S;
52
53#[cfg(not(test))]
54impl S {
55 fn foo3(&self) -> i32 { 0 }
56}
57
58#[cfg(test)]
59impl 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]
27fn infer_await() { 72fn 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"] }
23ra_syntax = { path = "../ra_syntax" } 23ra_syntax = { path = "../ra_syntax" }
24ra_text_edit = { path = "../ra_text_edit" } 24ra_text_edit = { path = "../ra_text_edit" }
25ra_db = { path = "../ra_db" } 25ra_db = { path = "../ra_db" }
26ra_cfg = { path = "../ra_cfg" }
26ra_fmt = { path = "../ra_fmt" } 27ra_fmt = { path = "../ra_fmt" }
27ra_prof = { path = "../ra_prof" } 28ra_prof = { path = "../ra_prof" }
28hir = { path = "../ra_hir", package = "ra_hir" } 29hir = { 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
3use ra_syntax::{ 3use ra_syntax::{ast, match_ast, AstNode};
4 algo::visit::{visitor_ctx, VisitorCtx},
5 ast, AstNode,
6};
7use rustc_hash::FxHashMap; 4use rustc_hash::FxHashMap;
8 5
9use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; 6use 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
3use ra_syntax::{ 3use 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 @@
3use hir::{AssocItem, FieldSource, HasSource, ModuleSource}; 3use hir::{AssocItem, FieldSource, HasSource, ModuleSource};
4use ra_db::{FileId, SourceDatabase}; 4use ra_db::{FileId, SourceDatabase};
5use ra_syntax::{ 5use 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 @@
3use crate::TextRange; 3use crate::TextRange;
4 4
5use ra_syntax::{ 5use 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
3use ra_db::{FileId, SourceDatabase}; 3use ra_db::{FileId, SourceDatabase};
4use ra_syntax::{ 4use 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
13use crate::{ 10use crate::{
@@ -114,91 +111,99 @@ pub(crate) fn name_definition(
114} 111}
115 112
116fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> { 113fn 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 @@
3use hir::{Adt, HasSource, HirDisplay}; 3use hir::{Adt, HasSource, HirDisplay};
4use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
5use ra_syntax::{ 5use 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
14use crate::{ 11use 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 @@
3use crate::{db::RootDatabase, FileId}; 3use crate::{db::RootDatabase, FileId};
4use hir::{HirDisplay, SourceAnalyzer, Ty}; 4use hir::{HirDisplay, SourceAnalyzer, Ty};
5use ra_syntax::{ 5use 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
87fn get_pat_type_hints( 89fn 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
50use std::sync::Arc; 50use std::sync::Arc;
51 51
52use ra_cfg::CfgOptions;
52use ra_db::{ 53use 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
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use ra_cfg::CfgOptions;
5use relative_path::RelativePathBuf; 6use relative_path::RelativePathBuf;
6use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; 7use 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};
34use ra_syntax::{ 34use 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
321fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> { 323fn 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"
19ra_vfs = "0.4.0" 19ra_vfs = "0.4.0"
20ra_syntax = { path = "../ra_syntax" } 20ra_syntax = { path = "../ra_syntax" }
21ra_db = { path = "../ra_db" } 21ra_db = { path = "../ra_db" }
22ra_cfg = { path = "../ra_cfg" }
22ra_text_edit = { path = "../ra_text_edit" } 23ra_text_edit = { path = "../ra_text_edit" }
23ra_ide_api = { path = "../ra_ide_api" } 24ra_ide_api = { path = "../ra_ide_api" }
24lsp-server = "0.2.0" 25lsp-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};
16use ra_project_model::ProjectWorkspace; 16use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace};
17use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; 17use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
18use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; 18use ra_vfs_glob::{Glob, RustPackageFilterBuilder};
19use relative_path::RelativePathBuf; 19use 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// }
265fn if_expr(p: &mut Parser) -> CompletedMarker { 262fn 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// }
313fn while_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { 311fn 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// }
360fn match_expr(p: &mut Parser) -> CompletedMarker { 360fn 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
13ra_arena = { path = "../ra_arena" } 13ra_arena = { path = "../ra_arena" }
14ra_db = { path = "../ra_db" } 14ra_db = { path = "../ra_db" }
15ra_cfg = { path = "../ra_cfg" }
15 16
16serde = { version = "1.0.89", features = ["derive"] } 17serde = { version = "1.0.89", features = ["derive"] }
17serde_json = "1.0.39" 18serde_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
3use std::path::PathBuf; 3use std::path::PathBuf;
4 4
5use rustc_hash::{FxHashMap, FxHashSet};
5use serde::Deserialize; 6use 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
15use ra_cfg::CfgOptions;
14use ra_db::{CrateGraph, CrateId, Edition, FileId}; 16use ra_db::{CrateGraph, CrateId, Edition, FileId};
15use rustc_hash::FxHashMap; 17use rustc_hash::FxHashMap;
16use serde_json::from_reader; 18use 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
310pub 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
3pub mod visit;
4
5use std::ops::RangeInclusive; 3use std::ops::RangeInclusive;
6 4
7use itertools::Itertools; 5use 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
3use crate::{AstNode, SyntaxNode};
4
5use std::marker::PhantomData;
6
7pub fn visitor<'a, T>() -> impl Visitor<'a, Output = T> {
8 EmptyVisitor { ph: PhantomData }
9}
10
11pub fn visitor_ctx<'a, T, C>(ctx: C) -> impl VisitorCtx<'a, Output = T, Ctx = C> {
12 EmptyVisitorCtx { ph: PhantomData, ctx }
13}
14
15pub 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
27pub 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)]
41struct EmptyVisitor<T> {
42 ph: PhantomData<fn() -> T>,
43}
44
45impl<'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)]
54struct EmptyVisitorCtx<T, C> {
55 ctx: C,
56 ph: PhantomData<fn() -> T>,
57}
58
59impl<'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)]
69pub struct Vis<V, N, F> {
70 inner: V,
71 f: F,
72 ph: PhantomData<fn(N)>,
73}
74
75impl<'a, V, N, F> Visitor<'a> for Vis<V, N, F>
76where
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)]
90pub struct VisCtx<V, N, F> {
91 inner: V,
92 f: F,
93 ph: PhantomData<fn(N)>,
94}
95
96impl<'a, V, N, F> VisitorCtx<'a> for VisCtx<V, N, F>
97where
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}
1965impl ast::AttrsOwner for ModuleItem {}
1965impl ModuleItem {} 1966impl ModuleItem {}
1966#[derive(Debug, Clone, PartialEq, Eq, Hash)] 1967#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1967pub struct Name { 1968pub 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)
3Grammar( 3Grammar(
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]
164macro_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;
5use rustc_lexer::unescape; 5use rustc_lexer::unescape;
6 6
7use crate::{ 7use 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 {
97pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { 96pub(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 @@
1fn foo() { 1fn 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 @@
1SOURCE_FILE@[0; 70) 1SOURCE_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 @@
1SOURCE_FILE@[0; 107) 1SOURCE_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 @@
1fn foo() { 1fn 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 @@
1SOURCE_FILE@[0; 47) 1SOURCE_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 @@
1fn foo() { let x = 1..; } 1fn 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 @@
1SOURCE_FILE@[0; 26) 1SOURCE_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"