aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_arena/src/lib.rs2
-rw-r--r--crates/ra_assists/src/auto_import.rs1
-rw-r--r--crates/ra_batch/Cargo.toml1
-rw-r--r--crates/ra_batch/src/lib.rs26
-rw-r--r--crates/ra_batch/src/vfs_filter.rs54
-rw-r--r--crates/ra_cli/src/analysis_stats.rs6
-rw-r--r--crates/ra_hir/Cargo.toml2
-rw-r--r--crates/ra_hir/src/code_model.rs4
-rw-r--r--crates/ra_hir/src/diagnostics.rs38
-rw-r--r--crates/ra_hir/src/expr.rs103
-rw-r--r--crates/ra_hir/src/marks.rs1
-rw-r--r--crates/ra_hir/src/nameres/collector.rs67
-rw-r--r--crates/ra_hir/src/nameres/raw.rs17
-rw-r--r--crates/ra_hir/src/nameres/tests/mods.rs14
-rw-r--r--crates/ra_hir/src/path.rs38
-rw-r--r--crates/ra_hir/src/source_binder.rs35
-rw-r--r--crates/ra_hir/src/ty.rs100
-rw-r--r--crates/ra_hir/src/ty/infer.rs47
-rw-r--r--crates/ra_hir/src/ty/lower.rs83
-rw-r--r--crates/ra_hir/src/ty/method_resolution.rs14
-rw-r--r--crates/ra_hir/src/ty/tests.rs121
-rw-r--r--crates/ra_hir/src/ty/traits.rs8
-rw-r--r--crates/ra_hir/src/ty/traits/chalk.rs51
-rw-r--r--crates/ra_ide_api/Cargo.toml2
-rw-r--r--crates/ra_ide_api/src/completion/complete_dot.rs65
-rw-r--r--crates/ra_ide_api/src/diagnostics.rs2
-rw-r--r--crates/ra_ide_api/src/inlay_hints.rs132
-rw-r--r--crates/ra_lsp_server/Cargo.toml1
-rw-r--r--crates/ra_lsp_server/src/config.rs (renamed from crates/ra_lsp_server/src/init.rs)15
-rw-r--r--crates/ra_lsp_server/src/conv.rs20
-rw-r--r--crates/ra_lsp_server/src/lib.rs6
-rw-r--r--crates/ra_lsp_server/src/main.rs4
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs21
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs144
-rw-r--r--crates/ra_lsp_server/src/markdown.rs17
-rw-r--r--crates/ra_lsp_server/src/req.rs28
-rw-r--r--crates/ra_lsp_server/src/vfs_filter.rs54
-rw-r--r--crates/ra_lsp_server/src/world.rs24
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/support.rs4
-rw-r--r--crates/ra_mbe/src/subtree_source.rs62
-rw-r--r--crates/ra_parser/src/grammar.rs10
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs11
-rw-r--r--crates/ra_parser/src/grammar/params.rs13
-rw-r--r--crates/ra_parser/src/grammar/type_args.rs7
-rw-r--r--crates/ra_parser/src/parser.rs54
-rw-r--r--crates/ra_project_model/Cargo.toml1
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs2
-rw-r--r--crates/ra_project_model/src/lib.rs57
-rw-r--r--crates/ra_project_model/src/sysroot.rs2
-rw-r--r--crates/ra_syntax/Cargo.toml1
-rw-r--r--crates/ra_syntax/src/ast.rs46
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs10
-rw-r--r--crates/ra_syntax/src/ast/generated.rs2
-rw-r--r--crates/ra_syntax/src/ast/traits.rs12
-rw-r--r--crates/ra_syntax/src/grammar.ron2
-rw-r--r--crates/ra_syntax/src/parsing/lexer.rs35
-rw-r--r--crates/ra_syntax/src/validation.rs25
-rw-r--r--crates/ra_syntax/src/validation/field_expr.rs13
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0010_bad_tuple_index_expr.txt7
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0011_field_expr.txt6
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.txt33
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0137_await_expr.txt6
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.txt55
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.txt23
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0139_param_outer_arg.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0139_param_outer_arg.txt32
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.rs21
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.txt477
-rw-r--r--crates/ra_tools/src/main.rs6
-rw-r--r--crates/ra_vfs_glob/Cargo.toml9
-rw-r--r--crates/ra_vfs_glob/src/lib.rs94
74 files changed, 1871 insertions, 639 deletions
diff --git a/crates/ra_arena/src/lib.rs b/crates/ra_arena/src/lib.rs
index 3b7cb77b1..3ec8d3b60 100644
--- a/crates/ra_arena/src/lib.rs
+++ b/crates/ra_arena/src/lib.rs
@@ -79,7 +79,7 @@ impl<ID: ArenaId, T> Arena<ID, T> {
79 self.data.push(value); 79 self.data.push(value);
80 ID::from_raw(id) 80 ID::from_raw(id)
81 } 81 }
82 pub fn iter(&self) -> impl Iterator<Item = (ID, &T)> { 82 pub fn iter(&self) -> impl Iterator<Item = (ID, &T)> + ExactSizeIterator {
83 self.data.iter().enumerate().map(|(idx, value)| (ID::from_raw(RawId(idx as u32)), value)) 83 self.data.iter().enumerate().map(|(idx, value)| (ID::from_raw(RawId(idx as u32)), value))
84 } 84 }
85} 85}
diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs
index a32e2f9b6..1158adbbc 100644
--- a/crates/ra_assists/src/auto_import.rs
+++ b/crates/ra_assists/src/auto_import.rs
@@ -71,6 +71,7 @@ fn compare_path_segment(a: &SmolStr, b: &ast::PathSegment) -> bool {
71 ast::PathSegmentKind::SelfKw => a == "self", 71 ast::PathSegmentKind::SelfKw => a == "self",
72 ast::PathSegmentKind::SuperKw => a == "super", 72 ast::PathSegmentKind::SuperKw => a == "super",
73 ast::PathSegmentKind::CrateKw => a == "crate", 73 ast::PathSegmentKind::CrateKw => a == "crate",
74 ast::PathSegmentKind::Type { .. } => false, // not allowed in imports
74 } 75 }
75 } else { 76 } else {
76 false 77 false
diff --git a/crates/ra_batch/Cargo.toml b/crates/ra_batch/Cargo.toml
index 0ee94c445..8e23826a4 100644
--- a/crates/ra_batch/Cargo.toml
+++ b/crates/ra_batch/Cargo.toml
@@ -9,6 +9,7 @@ log = "0.4.5"
9rustc-hash = "1.0" 9rustc-hash = "1.0"
10 10
11ra_vfs = "0.2.0" 11ra_vfs = "0.2.0"
12ra_vfs_glob = { path = "../ra_vfs_glob" }
12ra_db = { path = "../ra_db" } 13ra_db = { path = "../ra_db" }
13ra_ide_api = { path = "../ra_ide_api" } 14ra_ide_api = { path = "../ra_ide_api" }
14ra_hir = { path = "../ra_hir" } 15ra_hir = { path = "../ra_hir" }
diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs
index c25737aaa..0db751465 100644
--- a/crates/ra_batch/src/lib.rs
+++ b/crates/ra_batch/src/lib.rs
@@ -1,14 +1,12 @@
1mod vfs_filter;
2
3use std::{collections::HashSet, error::Error, path::Path}; 1use std::{collections::HashSet, error::Error, path::Path};
4 2
5use rustc_hash::FxHashMap; 3use rustc_hash::FxHashMap;
6 4
7use ra_db::{CrateGraph, FileId, SourceRootId}; 5use ra_db::{CrateGraph, FileId, SourceRootId};
8use ra_ide_api::{AnalysisChange, AnalysisHost}; 6use ra_ide_api::{AnalysisChange, AnalysisHost};
9use ra_project_model::{ProjectRoot, ProjectWorkspace}; 7use ra_project_model::{PackageRoot, ProjectWorkspace};
10use ra_vfs::{Vfs, VfsChange}; 8use ra_vfs::{RootEntry, Vfs, VfsChange};
11use vfs_filter::IncludeRustFiles; 9use ra_vfs_glob::RustPackageFilterBuilder;
12 10
13type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>; 11type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;
14 12
@@ -19,11 +17,23 @@ fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
19 SourceRootId(r.0) 17 SourceRootId(r.0)
20} 18}
21 19
22pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap<SourceRootId, ProjectRoot>)> { 20pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
23 let root = std::env::current_dir()?.join(root); 21 let root = std::env::current_dir()?.join(root);
24 let ws = ProjectWorkspace::discover(root.as_ref())?; 22 let ws = ProjectWorkspace::discover(root.as_ref())?;
25 let project_roots = ws.to_roots(); 23 let project_roots = ws.to_roots();
26 let (mut vfs, roots) = Vfs::new(IncludeRustFiles::from_roots(project_roots.clone()).collect()); 24 let (mut vfs, roots) = Vfs::new(
25 project_roots
26 .iter()
27 .map(|pkg_root| {
28 RootEntry::new(
29 pkg_root.path().clone(),
30 RustPackageFilterBuilder::default()
31 .set_member(pkg_root.is_member())
32 .into_vfs_filter(),
33 )
34 })
35 .collect(),
36 );
27 let crate_graph = ws.to_crate_graph(&mut |path: &Path| { 37 let crate_graph = ws.to_crate_graph(&mut |path: &Path| {
28 let vfs_file = vfs.load(path); 38 let vfs_file = vfs.load(path);
29 log::debug!("vfs file {:?} -> {:?}", path, vfs_file); 39 log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
@@ -48,7 +58,7 @@ pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap<SourceRootId,
48} 58}
49 59
50pub fn load( 60pub fn load(
51 source_roots: &FxHashMap<SourceRootId, ProjectRoot>, 61 source_roots: &FxHashMap<SourceRootId, PackageRoot>,
52 crate_graph: CrateGraph, 62 crate_graph: CrateGraph,
53 vfs: &mut Vfs, 63 vfs: &mut Vfs,
54) -> AnalysisHost { 64) -> AnalysisHost {
diff --git a/crates/ra_batch/src/vfs_filter.rs b/crates/ra_batch/src/vfs_filter.rs
deleted file mode 100644
index 2f0d8cb8b..000000000
--- a/crates/ra_batch/src/vfs_filter.rs
+++ /dev/null
@@ -1,54 +0,0 @@
1use ra_project_model::ProjectRoot;
2use ra_vfs::{Filter, RelativePath, RootEntry};
3use std::path::PathBuf;
4
5/// `IncludeRustFiles` is used to convert
6/// from `ProjectRoot` to `RootEntry` for VFS
7pub struct IncludeRustFiles {
8 root: ProjectRoot,
9}
10
11impl IncludeRustFiles {
12 pub fn from_roots<R>(roots: R) -> impl Iterator<Item = RootEntry>
13 where
14 R: IntoIterator<Item = ProjectRoot>,
15 {
16 roots.into_iter().map(IncludeRustFiles::from_root)
17 }
18
19 pub fn from_root(root: ProjectRoot) -> RootEntry {
20 IncludeRustFiles::from(root).into()
21 }
22
23 #[allow(unused)]
24 pub fn external(path: PathBuf) -> RootEntry {
25 IncludeRustFiles::from_root(ProjectRoot::new(path, false))
26 }
27
28 pub fn member(path: PathBuf) -> RootEntry {
29 IncludeRustFiles::from_root(ProjectRoot::new(path, true))
30 }
31}
32
33impl Filter for IncludeRustFiles {
34 fn include_dir(&self, dir_path: &RelativePath) -> bool {
35 self.root.include_dir(dir_path)
36 }
37
38 fn include_file(&self, file_path: &RelativePath) -> bool {
39 self.root.include_file(file_path)
40 }
41}
42
43impl From<ProjectRoot> for IncludeRustFiles {
44 fn from(v: ProjectRoot) -> IncludeRustFiles {
45 IncludeRustFiles { root: v }
46 }
47}
48
49impl From<IncludeRustFiles> for RootEntry {
50 fn from(v: IncludeRustFiles) -> RootEntry {
51 let path = v.root.path().clone();
52 RootEntry::new(path, Box::new(v))
53 }
54}
diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs
index 403aab352..7e7e6c073 100644
--- a/crates/ra_cli/src/analysis_stats.rs
+++ b/crates/ra_cli/src/analysis_stats.rs
@@ -52,6 +52,9 @@ pub fn run(verbose: bool, memory_usage: bool, path: &Path, only: Option<&str>) -
52 println!("Total modules found: {}", visited_modules.len()); 52 println!("Total modules found: {}", visited_modules.len());
53 println!("Total declarations: {}", num_decls); 53 println!("Total declarations: {}", num_decls);
54 println!("Total functions: {}", funcs.len()); 54 println!("Total functions: {}", funcs.len());
55 println!("Item Collection: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage());
56
57 let inference_time = Instant::now();
55 let bar = indicatif::ProgressBar::with_draw_target( 58 let bar = indicatif::ProgressBar::with_draw_target(
56 funcs.len() as u64, 59 funcs.len() as u64,
57 indicatif::ProgressDrawTarget::stderr_nohz(), 60 indicatif::ProgressDrawTarget::stderr_nohz(),
@@ -112,7 +115,8 @@ pub fn run(verbose: bool, memory_usage: bool, path: &Path, only: Option<&str>) -
112 num_exprs_partially_unknown, 115 num_exprs_partially_unknown,
113 (num_exprs_partially_unknown * 100 / num_exprs) 116 (num_exprs_partially_unknown * 100 / num_exprs)
114 ); 117 );
115 println!("Analysis: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage()); 118 println!("Inference: {:?}, {}", inference_time.elapsed(), ra_prof::memory_usage());
119 println!("Total: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage());
116 120
117 if memory_usage { 121 if memory_usage {
118 drop(db); 122 drop(db);
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
index 285b3c63a..1a2f1b47c 100644
--- a/crates/ra_hir/Cargo.toml
+++ b/crates/ra_hir/Cargo.toml
@@ -27,4 +27,4 @@ chalk-ir = { git = "https://github.com/rust-lang/chalk.git" }
27lalrpop-intern = "0.15.1" 27lalrpop-intern = "0.15.1"
28 28
29[dev-dependencies] 29[dev-dependencies]
30insta = "0.9.0" 30insta = "0.10.0"
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 779764590..89fc1d1a1 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -838,6 +838,10 @@ impl TypeAlias {
838 self.id.module(db) 838 self.id.module(db)
839 } 839 }
840 840
841 pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
842 self.module(db).krate(db)
843 }
844
841 /// The containing impl block, if this is a method. 845 /// The containing impl block, if this is a method.
842 pub fn impl_block(self, db: &impl DefDatabase) -> Option<ImplBlock> { 846 pub fn impl_block(self, db: &impl DefDatabase) -> Option<ImplBlock> {
843 let module_impls = db.impls_in_module(self.module(db)); 847 let module_impls = db.impls_in_module(self.module(db));
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs
index 0290483b3..f6240830f 100644
--- a/crates/ra_hir/src/diagnostics.rs
+++ b/crates/ra_hir/src/diagnostics.rs
@@ -3,7 +3,7 @@ use std::{any::Any, fmt};
3use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, TextRange}; 3use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, TextRange};
4use relative_path::RelativePathBuf; 4use relative_path::RelativePathBuf;
5 5
6use crate::{HirDatabase, HirFileId, Name}; 6use crate::{HirDatabase, HirFileId, Name, Source};
7 7
8/// Diagnostic defines hir API for errors and warnings. 8/// Diagnostic defines hir API for errors and warnings.
9/// 9///
@@ -19,10 +19,9 @@ use crate::{HirDatabase, HirFileId, Name};
19/// instance of `Diagnostic` on demand. 19/// instance of `Diagnostic` on demand.
20pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { 20pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
21 fn message(&self) -> String; 21 fn message(&self) -> String;
22 fn file(&self) -> HirFileId; 22 fn source(&self) -> Source<SyntaxNodePtr>;
23 fn syntax_node_ptr(&self) -> SyntaxNodePtr;
24 fn highlight_range(&self) -> TextRange { 23 fn highlight_range(&self) -> TextRange {
25 self.syntax_node_ptr().range() 24 self.source().ast.range()
26 } 25 }
27 fn as_any(&self) -> &(dyn Any + Send + 'static); 26 fn as_any(&self) -> &(dyn Any + Send + 'static);
28} 27}
@@ -34,8 +33,8 @@ pub trait AstDiagnostic {
34 33
35impl dyn Diagnostic { 34impl dyn Diagnostic {
36 pub fn syntax_node(&self, db: &impl HirDatabase) -> SyntaxNode { 35 pub fn syntax_node(&self, db: &impl HirDatabase) -> SyntaxNode {
37 let node = db.parse_or_expand(self.file()).unwrap(); 36 let node = db.parse_or_expand(self.source().file_id).unwrap();
38 self.syntax_node_ptr().to_node(&node) 37 self.source().ast.to_node(&node)
39 } 38 }
40 39
41 pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> { 40 pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
@@ -87,12 +86,11 @@ impl Diagnostic for NoSuchField {
87 fn message(&self) -> String { 86 fn message(&self) -> String {
88 "no such field".to_string() 87 "no such field".to_string()
89 } 88 }
90 fn file(&self) -> HirFileId { 89
91 self.file 90 fn source(&self) -> Source<SyntaxNodePtr> {
92 } 91 Source { file_id: self.file, ast: self.field.into() }
93 fn syntax_node_ptr(&self) -> SyntaxNodePtr {
94 self.field.into()
95 } 92 }
93
96 fn as_any(&self) -> &(dyn Any + Send + 'static) { 94 fn as_any(&self) -> &(dyn Any + Send + 'static) {
97 self 95 self
98 } 96 }
@@ -109,11 +107,8 @@ impl Diagnostic for UnresolvedModule {
109 fn message(&self) -> String { 107 fn message(&self) -> String {
110 "unresolved module".to_string() 108 "unresolved module".to_string()
111 } 109 }
112 fn file(&self) -> HirFileId { 110 fn source(&self) -> Source<SyntaxNodePtr> {
113 self.file 111 Source { file_id: self.file, ast: self.decl.into() }
114 }
115 fn syntax_node_ptr(&self) -> SyntaxNodePtr {
116 self.decl.into()
117 } 112 }
118 fn as_any(&self) -> &(dyn Any + Send + 'static) { 113 fn as_any(&self) -> &(dyn Any + Send + 'static) {
119 self 114 self
@@ -131,11 +126,8 @@ impl Diagnostic for MissingFields {
131 fn message(&self) -> String { 126 fn message(&self) -> String {
132 "fill structure fields".to_string() 127 "fill structure fields".to_string()
133 } 128 }
134 fn file(&self) -> HirFileId { 129 fn source(&self) -> Source<SyntaxNodePtr> {
135 self.file 130 Source { file_id: self.file, ast: self.field_list.into() }
136 }
137 fn syntax_node_ptr(&self) -> SyntaxNodePtr {
138 self.field_list.into()
139 } 131 }
140 fn as_any(&self) -> &(dyn Any + Send + 'static) { 132 fn as_any(&self) -> &(dyn Any + Send + 'static) {
141 self 133 self
@@ -146,8 +138,8 @@ impl AstDiagnostic for MissingFields {
146 type AST = ast::NamedFieldList; 138 type AST = ast::NamedFieldList;
147 139
148 fn ast(&self, db: &impl HirDatabase) -> Self::AST { 140 fn ast(&self, db: &impl HirDatabase) -> Self::AST {
149 let root = db.parse_or_expand(self.file()).unwrap(); 141 let root = db.parse_or_expand(self.source().file_id).unwrap();
150 let node = self.syntax_node_ptr().to_node(&root); 142 let node = self.source().ast.to_node(&root);
151 ast::NamedFieldList::cast(node).unwrap() 143 ast::NamedFieldList::cast(node).unwrap()
152 } 144 }
153} 145}
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 4dcea19a9..f33676655 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -11,17 +11,16 @@ use ra_syntax::{
11 }, 11 },
12 AstNode, AstPtr, SyntaxNodePtr, 12 AstNode, AstPtr, SyntaxNodePtr,
13}; 13};
14use test_utils::tested_by;
14 15
15use crate::{ 16use crate::{
16 name::{AsName, SELF_PARAM}, 17 name::{AsName, SELF_PARAM},
18 path::GenericArgs,
19 ty::primitive::{FloatTy, IntTy, UncertainFloatTy, UncertainIntTy},
17 type_ref::{Mutability, TypeRef}, 20 type_ref::{Mutability, TypeRef},
18 DefWithBody, Either, HasSource, HirDatabase, HirFileId, MacroCallLoc, MacroFileKind, Name, 21 DefWithBody, Either, HasSource, HirDatabase, HirFileId, MacroCallLoc, MacroFileKind, Name,
19 Path, Resolver, 22 Path, Resolver,
20}; 23};
21use crate::{
22 path::GenericArgs,
23 ty::primitive::{FloatTy, IntTy, UncertainFloatTy, UncertainIntTy},
24};
25 24
26pub use self::scope::ExprScopes; 25pub use self::scope::ExprScopes;
27 26
@@ -558,37 +557,40 @@ where
558 let syntax_ptr = SyntaxNodePtr::new(expr.syntax()); 557 let syntax_ptr = SyntaxNodePtr::new(expr.syntax());
559 match expr.kind() { 558 match expr.kind() {
560 ast::ExprKind::IfExpr(e) => { 559 ast::ExprKind::IfExpr(e) => {
561 if let Some(pat) = e.condition().and_then(|c| c.pat()) { 560 let then_branch = self.collect_block_opt(e.then_branch());
562 // if let -- desugar to match 561
563 let pat = self.collect_pat(pat); 562 let else_branch = e.else_branch().map(|b| match b {
564 let match_expr = 563 ast::ElseBranch::Block(it) => self.collect_block(it),
565 self.collect_expr_opt(e.condition().expect("checked above").expr()); 564 ast::ElseBranch::IfExpr(elif) => {
566 let then_branch = self.collect_block_opt(e.then_branch()); 565 let expr: ast::Expr = ast::Expr::cast(elif.syntax().clone()).unwrap();
567 let else_branch = e 566 self.collect_expr(expr)
568 .else_branch() 567 }
569 .map(|b| match b { 568 });
570 ast::ElseBranch::Block(it) => self.collect_block(it), 569
571 ast::ElseBranch::IfExpr(elif) => self.collect_expr(elif.into()), 570 let condition = match e.condition() {
572 }) 571 None => self.exprs.alloc(Expr::Missing),
573 .unwrap_or_else(|| self.empty_block()); 572 Some(condition) => match condition.pat() {
574 let placeholder_pat = self.pats.alloc(Pat::Missing); 573 None => self.collect_expr_opt(condition.expr()),
575 let arms = vec![ 574 // if let -- desugar to match
576 MatchArm { pats: vec![pat], expr: then_branch, guard: None }, 575 Some(pat) => {
577 MatchArm { pats: vec![placeholder_pat], expr: else_branch, guard: None }, 576 let pat = self.collect_pat(pat);
578 ]; 577 let match_expr = self.collect_expr_opt(condition.expr());
579 self.alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr) 578 let placeholder_pat = self.pats.alloc(Pat::Missing);
580 } else { 579 let arms = vec![
581 let condition = self.collect_expr_opt(e.condition().and_then(|c| c.expr())); 580 MatchArm { pats: vec![pat], expr: then_branch, guard: None },
582 let then_branch = self.collect_block_opt(e.then_branch()); 581 MatchArm {
583 let else_branch = e.else_branch().map(|b| match b { 582 pats: vec![placeholder_pat],
584 ast::ElseBranch::Block(it) => self.collect_block(it), 583 expr: else_branch.unwrap_or_else(|| self.empty_block()),
585 ast::ElseBranch::IfExpr(elif) => { 584 guard: None,
586 let expr: ast::Expr = ast::Expr::cast(elif.syntax().clone()).unwrap(); 585 },
587 self.collect_expr(expr) 586 ];
587 return self
588 .alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr);
588 } 589 }
589 }); 590 },
590 self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr) 591 };
591 } 592
593 self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
592 } 594 }
593 ast::ExprKind::TryBlockExpr(e) => { 595 ast::ExprKind::TryBlockExpr(e) => {
594 let body = self.collect_block_opt(e.try_body()); 596 let body = self.collect_block_opt(e.try_body());
@@ -600,17 +602,30 @@ where
600 self.alloc_expr(Expr::Loop { body }, syntax_ptr) 602 self.alloc_expr(Expr::Loop { body }, syntax_ptr)
601 } 603 }
602 ast::ExprKind::WhileExpr(e) => { 604 ast::ExprKind::WhileExpr(e) => {
603 let condition = if let Some(condition) = e.condition() {
604 if condition.pat().is_none() {
605 self.collect_expr_opt(condition.expr())
606 } else {
607 // FIXME handle while let
608 return self.alloc_expr(Expr::Missing, syntax_ptr);
609 }
610 } else {
611 self.exprs.alloc(Expr::Missing)
612 };
613 let body = self.collect_block_opt(e.loop_body()); 605 let body = self.collect_block_opt(e.loop_body());
606
607 let condition = match e.condition() {
608 None => self.exprs.alloc(Expr::Missing),
609 Some(condition) => match condition.pat() {
610 None => self.collect_expr_opt(condition.expr()),
611 // if let -- desugar to match
612 Some(pat) => {
613 tested_by!(infer_while_let);
614 let pat = self.collect_pat(pat);
615 let match_expr = self.collect_expr_opt(condition.expr());
616 let placeholder_pat = self.pats.alloc(Pat::Missing);
617 let break_ = self.exprs.alloc(Expr::Break { expr: None });
618 let arms = vec![
619 MatchArm { pats: vec![pat], expr: body, guard: None },
620 MatchArm { pats: vec![placeholder_pat], expr: break_, guard: None },
621 ];
622 let match_expr =
623 self.exprs.alloc(Expr::Match { expr: match_expr, arms });
624 return self.alloc_expr(Expr::Loop { body: match_expr }, syntax_ptr);
625 }
626 },
627 };
628
614 self.alloc_expr(Expr::While { condition, body }, syntax_ptr) 629 self.alloc_expr(Expr::While { condition, body }, syntax_ptr)
615 } 630 }
616 ast::ExprKind::ForExpr(e) => { 631 ast::ExprKind::ForExpr(e) => {
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs
index 2d831f0d8..5b15eee90 100644
--- a/crates/ra_hir/src/marks.rs
+++ b/crates/ra_hir/src/marks.rs
@@ -10,4 +10,5 @@ test_utils::marks!(
10 std_prelude 10 std_prelude
11 match_ergonomics_ref 11 match_ergonomics_ref
12 trait_resolution_on_fn_type 12 trait_resolution_on_fn_type
13 infer_while_let
13); 14);
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs
index 06b732215..7da2dcdff 100644
--- a/crates/ra_hir/src/nameres/collector.rs
+++ b/crates/ra_hir/src/nameres/collector.rs
@@ -483,7 +483,7 @@ struct ModCollector<'a, D> {
483 module_id: CrateModuleId, 483 module_id: CrateModuleId,
484 file_id: HirFileId, 484 file_id: HirFileId,
485 raw_items: &'a raw::RawItems, 485 raw_items: &'a raw::RawItems,
486 parent_module: Option<&'a Name>, 486 parent_module: Option<ParentModule<'a>>,
487} 487}
488 488
489impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> 489impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>>
@@ -508,15 +508,16 @@ where
508 fn collect_module(&mut self, module: &raw::ModuleData) { 508 fn collect_module(&mut self, module: &raw::ModuleData) {
509 match module { 509 match module {
510 // inline module, just recurse 510 // inline module, just recurse
511 raw::ModuleData::Definition { name, items, ast_id } => { 511 raw::ModuleData::Definition { name, items, ast_id, attr_path } => {
512 let module_id = 512 let module_id =
513 self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None); 513 self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None);
514 let parent_module = ParentModule { name, attr_path: attr_path.as_ref() };
514 ModCollector { 515 ModCollector {
515 def_collector: &mut *self.def_collector, 516 def_collector: &mut *self.def_collector,
516 module_id, 517 module_id,
517 file_id: self.file_id, 518 file_id: self.file_id,
518 raw_items: self.raw_items, 519 raw_items: self.raw_items,
519 parent_module: Some(name), 520 parent_module: Some(parent_module),
520 } 521 }
521 .collect(&*items); 522 .collect(&*items);
522 } 523 }
@@ -530,7 +531,7 @@ where
530 name, 531 name,
531 is_root, 532 is_root,
532 attr_path.as_ref(), 533 attr_path.as_ref(),
533 self.parent_module, 534 self.parent_module.as_ref(),
534 ) { 535 ) {
535 Ok(file_id) => { 536 Ok(file_id) => {
536 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); 537 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id));
@@ -647,7 +648,7 @@ fn resolve_submodule(
647 name: &Name, 648 name: &Name,
648 is_root: bool, 649 is_root: bool,
649 attr_path: Option<&SmolStr>, 650 attr_path: Option<&SmolStr>,
650 parent_module: Option<&Name>, 651 parent_module: Option<&ParentModule>,
651) -> Result<FileId, RelativePathBuf> { 652) -> Result<FileId, RelativePathBuf> {
652 let file_id = file_id.original_file(db); 653 let file_id = file_id.original_file(db);
653 let source_root_id = db.file_source_root(file_id); 654 let source_root_id = db.file_source_root(file_id);
@@ -657,20 +658,49 @@ fn resolve_submodule(
657 let mod_name = path.file_stem().unwrap_or("unknown"); 658 let mod_name = path.file_stem().unwrap_or("unknown");
658 659
659 let resolve_mode = match (attr_path.filter(|p| !p.is_empty()), parent_module) { 660 let resolve_mode = match (attr_path.filter(|p| !p.is_empty()), parent_module) {
660 (Some(file_path), Some(parent_name)) => { 661 (Some(file_path), Some(parent_module)) => {
661 let file_path = normalize_attribute_path(file_path); 662 let file_path = normalize_attribute_path(file_path);
662 let path = dir_path.join(format!("{}/{}", parent_name, file_path)).normalize(); 663 match parent_module.attribute_path() {
663 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::WithAttributePath(path)) 664 Some(parent_module_attr_path) => {
665 let path = dir_path
666 .join(format!(
667 "{}/{}",
668 normalize_attribute_path(parent_module_attr_path),
669 file_path
670 ))
671 .normalize();
672 ResolutionMode::InlineModuleWithAttributePath(
673 InsideInlineModuleMode::WithAttributePath(path),
674 )
675 }
676 None => {
677 let path =
678 dir_path.join(format!("{}/{}", parent_module.name, file_path)).normalize();
679 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::WithAttributePath(
680 path,
681 ))
682 }
683 }
664 } 684 }
685 (None, Some(parent_module)) => match parent_module.attribute_path() {
686 Some(parent_module_attr_path) => {
687 let path = dir_path.join(format!(
688 "{}/{}.rs",
689 normalize_attribute_path(parent_module_attr_path),
690 name
691 ));
692 ResolutionMode::InlineModuleWithAttributePath(InsideInlineModuleMode::File(path))
693 }
694 None => {
695 let path = dir_path.join(format!("{}/{}.rs", parent_module.name, name));
696 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::File(path))
697 }
698 },
665 (Some(file_path), None) => { 699 (Some(file_path), None) => {
666 let file_path = normalize_attribute_path(file_path); 700 let file_path = normalize_attribute_path(file_path);
667 let path = dir_path.join(file_path.as_ref()).normalize(); 701 let path = dir_path.join(file_path.as_ref()).normalize();
668 ResolutionMode::OutOfLine(OutOfLineMode::WithAttributePath(path)) 702 ResolutionMode::OutOfLine(OutOfLineMode::WithAttributePath(path))
669 } 703 }
670 (None, Some(parent_name)) => {
671 let path = dir_path.join(format!("{}/{}.rs", parent_name, name));
672 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::File(path))
673 }
674 _ => { 704 _ => {
675 let is_dir_owner = is_root || mod_name == "mod"; 705 let is_dir_owner = is_root || mod_name == "mod";
676 if is_dir_owner { 706 if is_dir_owner {
@@ -743,6 +773,7 @@ impl InsideInlineModuleMode {
743enum ResolutionMode { 773enum ResolutionMode {
744 OutOfLine(OutOfLineMode), 774 OutOfLine(OutOfLineMode),
745 InsideInlineModule(InsideInlineModuleMode), 775 InsideInlineModule(InsideInlineModuleMode),
776 InlineModuleWithAttributePath(InsideInlineModuleMode),
746} 777}
747 778
748impl ResolutionMode { 779impl ResolutionMode {
@@ -752,6 +783,7 @@ impl ResolutionMode {
752 match self { 783 match self {
753 OutOfLine(mode) => mode.resolve(source_root), 784 OutOfLine(mode) => mode.resolve(source_root),
754 InsideInlineModule(mode) => mode.resolve(source_root), 785 InsideInlineModule(mode) => mode.resolve(source_root),
786 InlineModuleWithAttributePath(mode) => mode.resolve(source_root),
755 } 787 }
756 } 788 }
757} 789}
@@ -773,6 +805,17 @@ fn resolve_find_result(
773 } 805 }
774} 806}
775 807
808struct ParentModule<'a> {
809 name: &'a Name,
810 attr_path: Option<&'a SmolStr>,
811}
812
813impl<'a> ParentModule<'a> {
814 pub fn attribute_path(&self) -> Option<&SmolStr> {
815 self.attr_path.filter(|p| !p.is_empty())
816 }
817}
818
776#[cfg(test)] 819#[cfg(test)]
777mod tests { 820mod tests {
778 use ra_db::SourceDatabase; 821 use ra_db::SourceDatabase;
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs
index 8517f3c43..584e15e29 100644
--- a/crates/ra_hir/src/nameres/raw.rs
+++ b/crates/ra_hir/src/nameres/raw.rs
@@ -130,8 +130,17 @@ impl_arena_id!(Module);
130 130
131#[derive(Debug, PartialEq, Eq)] 131#[derive(Debug, PartialEq, Eq)]
132pub(super) enum ModuleData { 132pub(super) enum ModuleData {
133 Declaration { name: Name, ast_id: FileAstId<ast::Module>, attr_path: Option<SmolStr> }, 133 Declaration {
134 Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> }, 134 name: Name,
135 ast_id: FileAstId<ast::Module>,
136 attr_path: Option<SmolStr>,
137 },
138 Definition {
139 name: Name,
140 ast_id: FileAstId<ast::Module>,
141 items: Vec<RawItem>,
142 attr_path: Option<SmolStr>,
143 },
135} 144}
136 145
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 146#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -256,9 +265,9 @@ impl RawItemsCollector {
256 None => return, 265 None => return,
257 }; 266 };
258 267
259 let attr_path = extract_mod_path_attribute(&module);
260 let ast_id = self.source_ast_id_map.ast_id(&module); 268 let ast_id = self.source_ast_id_map.ast_id(&module);
261 if module.has_semi() { 269 if module.has_semi() {
270 let attr_path = extract_mod_path_attribute(&module);
262 let item = 271 let item =
263 self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id, attr_path }); 272 self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id, attr_path });
264 self.push_item(current_module, RawItem::Module(item)); 273 self.push_item(current_module, RawItem::Module(item));
@@ -266,10 +275,12 @@ impl RawItemsCollector {
266 } 275 }
267 276
268 if let Some(item_list) = module.item_list() { 277 if let Some(item_list) = module.item_list() {
278 let attr_path = extract_mod_path_attribute(&module);
269 let item = self.raw_items.modules.alloc(ModuleData::Definition { 279 let item = self.raw_items.modules.alloc(ModuleData::Definition {
270 name, 280 name,
271 ast_id, 281 ast_id,
272 items: Vec::new(), 282 items: Vec::new(),
283 attr_path,
273 }); 284 });
274 self.process_module(Some(item), item_list); 285 self.process_module(Some(item), item_list);
275 self.push_item(current_module, RawItem::Module(item)); 286 self.push_item(current_module, RawItem::Module(item));
diff --git a/crates/ra_hir/src/nameres/tests/mods.rs b/crates/ra_hir/src/nameres/tests/mods.rs
index 382728149..6dd18df1a 100644
--- a/crates/ra_hir/src/nameres/tests/mods.rs
+++ b/crates/ra_hir/src/nameres/tests/mods.rs
@@ -336,9 +336,7 @@ fn module_resolution_explicit_path_mod_rs_with_win_separator() {
336 "###); 336 "###);
337} 337}
338 338
339// FIXME: issue #1529. not support out-of-line modules inside inline.
340#[test] 339#[test]
341#[ignore]
342fn module_resolution_decl_inside_inline_module_with_path_attribute() { 340fn module_resolution_decl_inside_inline_module_with_path_attribute() {
343 let map = def_map_with_crate_graph( 341 let map = def_map_with_crate_graph(
344 r###" 342 r###"
@@ -397,9 +395,7 @@ fn module_resolution_decl_inside_inline_module() {
397 "###); 395 "###);
398} 396}
399 397
400// FIXME: issue #1529. not support out-of-line modules inside inline.
401#[test] 398#[test]
402#[ignore]
403fn module_resolution_decl_inside_inline_module_2_with_path_attribute() { 399fn module_resolution_decl_inside_inline_module_2_with_path_attribute() {
404 let map = def_map_with_crate_graph( 400 let map = def_map_with_crate_graph(
405 r###" 401 r###"
@@ -429,9 +425,7 @@ fn module_resolution_decl_inside_inline_module_2_with_path_attribute() {
429 "###); 425 "###);
430} 426}
431 427
432// FIXME: issue #1529. not support out-of-line modules inside inline.
433#[test] 428#[test]
434#[ignore]
435fn module_resolution_decl_inside_inline_module_3() { 429fn module_resolution_decl_inside_inline_module_3() {
436 let map = def_map_with_crate_graph( 430 let map = def_map_with_crate_graph(
437 r###" 431 r###"
@@ -462,9 +456,7 @@ fn module_resolution_decl_inside_inline_module_3() {
462 "###); 456 "###);
463} 457}
464 458
465// FIXME: issue #1529. not support out-of-line modules inside inline.
466#[test] 459#[test]
467#[ignore]
468fn module_resolution_decl_inside_inline_module_empty_path() { 460fn module_resolution_decl_inside_inline_module_empty_path() {
469 let map = def_map_with_crate_graph( 461 let map = def_map_with_crate_graph(
470 r###" 462 r###"
@@ -475,7 +467,7 @@ fn module_resolution_decl_inside_inline_module_empty_path() {
475 mod bar; 467 mod bar;
476 } 468 }
477 469
478 //- /users.rs 470 //- /foo/users.rs
479 pub struct Baz; 471 pub struct Baz;
480 "###, 472 "###,
481 crate_graph! { 473 crate_graph! {
@@ -520,9 +512,7 @@ fn module_resolution_decl_empty_path() {
520 "###); 512 "###);
521} 513}
522 514
523// FIXME: issue #1529. not support out-of-line modules inside inline.
524#[test] 515#[test]
525#[ignore]
526fn module_resolution_decl_inside_inline_module_relative_path() { 516fn module_resolution_decl_inside_inline_module_relative_path() {
527 let map = def_map_with_crate_graph( 517 let map = def_map_with_crate_graph(
528 r###" 518 r###"
@@ -660,9 +650,7 @@ fn module_resolution_decl_inside_inline_module_in_non_crate_root() {
660 "###); 650 "###);
661} 651}
662 652
663// FIXME: issue #1529. not support out-of-line modules inside inline.
664#[test] 653#[test]
665#[ignore]
666fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() { 654fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() {
667 let map = def_map_with_crate_graph( 655 let map = def_map_with_crate_graph(
668 r###" 656 r###"
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs
index 882db7681..5ee71e421 100644
--- a/crates/ra_hir/src/path.rs
+++ b/crates/ra_hir/src/path.rs
@@ -25,6 +25,12 @@ pub struct PathSegment {
25#[derive(Debug, Clone, PartialEq, Eq, Hash)] 25#[derive(Debug, Clone, PartialEq, Eq, Hash)]
26pub struct GenericArgs { 26pub struct GenericArgs {
27 pub args: Vec<GenericArg>, 27 pub args: Vec<GenericArg>,
28 /// This specifies whether the args contain a Self type as the first
29 /// element. This is the case for path segments like `<T as Trait>`, where
30 /// `T` is actually a type parameter for the path `Trait` specifying the
31 /// Self type. Otherwise, when we have a path `Trait<X, Y>`, the Self type
32 /// is left out.
33 pub has_self_type: bool,
28 // someday also bindings 34 // someday also bindings
29} 35}
30 36
@@ -74,6 +80,28 @@ impl Path {
74 let segment = PathSegment { name: name.as_name(), args_and_bindings: args }; 80 let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
75 segments.push(segment); 81 segments.push(segment);
76 } 82 }
83 ast::PathSegmentKind::Type { type_ref, trait_ref } => {
84 assert!(path.qualifier().is_none()); // this can only occur at the first segment
85
86 // FIXME: handle <T> syntax (type segments without trait)
87
88 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
89 let path = Path::from_ast(trait_ref?.path()?)?;
90 kind = path.kind;
91 let mut prefix_segments = path.segments;
92 prefix_segments.reverse();
93 segments.extend(prefix_segments);
94 // Insert the type reference (T in the above example) as Self parameter for the trait
95 let self_type = TypeRef::from_ast(type_ref?);
96 let mut last_segment = segments.last_mut()?;
97 if last_segment.args_and_bindings.is_none() {
98 last_segment.args_and_bindings = Some(Arc::new(GenericArgs::empty()));
99 };
100 let args = last_segment.args_and_bindings.as_mut().unwrap();
101 let mut args_inner = Arc::make_mut(args);
102 args_inner.has_self_type = true;
103 args_inner.args.insert(0, GenericArg::Type(self_type));
104 }
77 ast::PathSegmentKind::CrateKw => { 105 ast::PathSegmentKind::CrateKw => {
78 kind = PathKind::Crate; 106 kind = PathKind::Crate;
79 break; 107 break;
@@ -144,11 +172,15 @@ impl GenericArgs {
144 } 172 }
145 // lifetimes and assoc type args ignored for now 173 // lifetimes and assoc type args ignored for now
146 if !args.is_empty() { 174 if !args.is_empty() {
147 Some(GenericArgs { args }) 175 Some(GenericArgs { args, has_self_type: false })
148 } else { 176 } else {
149 None 177 None
150 } 178 }
151 } 179 }
180
181 pub(crate) fn empty() -> GenericArgs {
182 GenericArgs { args: Vec::new(), has_self_type: false }
183 }
152} 184}
153 185
154impl From<Name> for Path { 186impl From<Name> for Path {
@@ -236,6 +268,10 @@ fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
236 } 268 }
237 Path { kind: PathKind::Super, segments: Vec::new() } 269 Path { kind: PathKind::Super, segments: Vec::new() }
238 } 270 }
271 ast::PathSegmentKind::Type { .. } => {
272 // not allowed in imports
273 return None;
274 }
239 }; 275 };
240 Some(res) 276 Some(res)
241} 277}
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index c2c6921cb..e86716d74 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -18,14 +18,18 @@ use ra_syntax::{
18use rustc_hash::{FxHashMap, FxHashSet}; 18use rustc_hash::{FxHashMap, FxHashSet};
19 19
20use crate::{ 20use crate::{
21 expr,
22 expr::{ 21 expr::{
22 self,
23 scope::{ExprScopes, ScopeId}, 23 scope::{ExprScopes, ScopeId},
24 BodySourceMap, 24 BodySourceMap,
25 }, 25 },
26 ids::LocationCtx, 26 ids::LocationCtx,
27 name,
28 path::{PathKind, PathSegment},
29 ty::method_resolution::implements_trait,
27 AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId, 30 AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId,
28 MacroDef, Module, Name, Path, PerNs, Resolver, Static, Struct, Trait, Ty, 31 MacroDef, Module, ModuleDef, Name, Path, PerNs, Resolution, Resolver, Static, Struct, Trait,
32 Ty,
29}; 33};
30 34
31/// Locates the module by `FileId`. Picks topmost module in the file. 35/// Locates the module by `FileId`. Picks topmost module in the file.
@@ -409,6 +413,33 @@ impl SourceAnalyzer {
409 crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value) 413 crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value)
410 } 414 }
411 415
416 /// Checks that particular type `ty` implements `std::future::Future`.
417 /// This function is used in `.await` syntax completion.
418 pub fn impls_future(&self, db: &impl HirDatabase, ty: Ty) -> bool {
419 let std_future_path = Path {
420 kind: PathKind::Abs,
421 segments: vec![
422 PathSegment { name: name::STD, args_and_bindings: None },
423 PathSegment { name: name::FUTURE_MOD, args_and_bindings: None },
424 PathSegment { name: name::FUTURE_TYPE, args_and_bindings: None },
425 ],
426 };
427
428 let std_future_trait =
429 match self.resolver.resolve_path_segments(db, &std_future_path).into_fully_resolved() {
430 PerNs { types: Some(Resolution::Def(ModuleDef::Trait(trait_))), .. } => trait_,
431 _ => return false,
432 };
433
434 let krate = match self.resolver.krate() {
435 Some(krate) => krate,
436 _ => return false,
437 };
438
439 let canonical_ty = crate::ty::Canonical { value: ty, num_vars: 0 };
440 implements_trait(&canonical_ty, db, &self.resolver, krate, std_future_trait)
441 }
442
412 #[cfg(test)] 443 #[cfg(test)]
413 pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { 444 pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> {
414 self.body_source_map.clone().unwrap() 445 self.body_source_map.clone().unwrap()
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index 82589e504..642dd02cb 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -94,6 +94,12 @@ pub enum TypeCtor {
94 94
95 /// A tuple type. For example, `(i32, bool)`. 95 /// A tuple type. For example, `(i32, bool)`.
96 Tuple { cardinality: u16 }, 96 Tuple { cardinality: u16 },
97
98 /// Represents an associated item like `Iterator::Item`. This is used
99 /// when we have tried to normalize a projection like `T::Item` but
100 /// couldn't find a better representation. In that case, we generate
101 /// an **application type** like `(Iterator::Item)<T>`.
102 AssociatedType(TypeAlias),
97} 103}
98 104
99/// A nominal type with (maybe 0) type parameters. This might be a primitive 105/// A nominal type with (maybe 0) type parameters. This might be a primitive
@@ -114,6 +120,12 @@ pub struct ProjectionTy {
114 pub parameters: Substs, 120 pub parameters: Substs,
115} 121}
116 122
123#[derive(Clone, PartialEq, Eq, Debug, Hash)]
124pub struct UnselectedProjectionTy {
125 pub type_name: Name,
126 pub parameters: Substs,
127}
128
117/// A type. 129/// A type.
118/// 130///
119/// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents 131/// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents
@@ -127,6 +139,18 @@ pub enum Ty {
127 /// several other things. 139 /// several other things.
128 Apply(ApplicationTy), 140 Apply(ApplicationTy),
129 141
142 /// A "projection" type corresponds to an (unnormalized)
143 /// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
144 /// trait and all its parameters are fully known.
145 Projection(ProjectionTy),
146
147 /// This is a variant of a projection in which the trait is
148 /// **not** known. It corresponds to a case where people write
149 /// `T::Item` without specifying the trait. We would then try to
150 /// figure out the trait by looking at all the traits that are in
151 /// scope.
152 UnselectedProjection(UnselectedProjectionTy),
153
130 /// A type parameter; for example, `T` in `fn f<T>(x: T) {} 154 /// A type parameter; for example, `T` in `fn f<T>(x: T) {}
131 Param { 155 Param {
132 /// The index of the parameter (starting with parameters from the 156 /// The index of the parameter (starting with parameters from the
@@ -352,6 +376,16 @@ impl Ty {
352 t.walk(f); 376 t.walk(f);
353 } 377 }
354 } 378 }
379 Ty::Projection(p_ty) => {
380 for t in p_ty.parameters.iter() {
381 t.walk(f);
382 }
383 }
384 Ty::UnselectedProjection(p_ty) => {
385 for t in p_ty.parameters.iter() {
386 t.walk(f);
387 }
388 }
355 Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} 389 Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
356 } 390 }
357 f(self); 391 f(self);
@@ -362,6 +396,12 @@ impl Ty {
362 Ty::Apply(a_ty) => { 396 Ty::Apply(a_ty) => {
363 a_ty.parameters.walk_mut(f); 397 a_ty.parameters.walk_mut(f);
364 } 398 }
399 Ty::Projection(p_ty) => {
400 p_ty.parameters.walk_mut(f);
401 }
402 Ty::UnselectedProjection(p_ty) => {
403 p_ty.parameters.walk_mut(f);
404 }
365 Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} 405 Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
366 } 406 }
367 f(self); 407 f(self);
@@ -572,15 +612,61 @@ impl HirDisplay for ApplicationTy {
572 write!(f, ">")?; 612 write!(f, ">")?;
573 } 613 }
574 } 614 }
615 TypeCtor::AssociatedType(type_alias) => {
616 let trait_name = type_alias
617 .parent_trait(f.db)
618 .and_then(|t| t.name(f.db))
619 .unwrap_or_else(Name::missing);
620 let name = type_alias.name(f.db);
621 write!(f, "{}::{}", trait_name, name)?;
622 if self.parameters.len() > 0 {
623 write!(f, "<")?;
624 f.write_joined(&*self.parameters.0, ", ")?;
625 write!(f, ">")?;
626 }
627 }
575 } 628 }
576 Ok(()) 629 Ok(())
577 } 630 }
578} 631}
579 632
633impl HirDisplay for ProjectionTy {
634 fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
635 let trait_name = self
636 .associated_ty
637 .parent_trait(f.db)
638 .and_then(|t| t.name(f.db))
639 .unwrap_or_else(Name::missing);
640 write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?;
641 if self.parameters.len() > 1 {
642 write!(f, "<")?;
643 f.write_joined(&self.parameters[1..], ", ")?;
644 write!(f, ">")?;
645 }
646 write!(f, ">::{}", self.associated_ty.name(f.db))?;
647 Ok(())
648 }
649}
650
651impl HirDisplay for UnselectedProjectionTy {
652 fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
653 write!(f, "{}", self.parameters[0].display(f.db))?;
654 if self.parameters.len() > 1 {
655 write!(f, "<")?;
656 f.write_joined(&self.parameters[1..], ", ")?;
657 write!(f, ">")?;
658 }
659 write!(f, "::{}", self.type_name)?;
660 Ok(())
661 }
662}
663
580impl HirDisplay for Ty { 664impl HirDisplay for Ty {
581 fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { 665 fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
582 match self { 666 match self {
583 Ty::Apply(a_ty) => a_ty.hir_fmt(f)?, 667 Ty::Apply(a_ty) => a_ty.hir_fmt(f)?,
668 Ty::Projection(p_ty) => p_ty.hir_fmt(f)?,
669 Ty::UnselectedProjection(p_ty) => p_ty.hir_fmt(f)?,
584 Ty::Param { name, .. } => write!(f, "{}", name)?, 670 Ty::Param { name, .. } => write!(f, "{}", name)?,
585 Ty::Bound(idx) => write!(f, "?{}", idx)?, 671 Ty::Bound(idx) => write!(f, "?{}", idx)?,
586 Ty::Unknown => write!(f, "{{unknown}}")?, 672 Ty::Unknown => write!(f, "{{unknown}}")?,
@@ -606,3 +692,17 @@ impl HirDisplay for TraitRef {
606 Ok(()) 692 Ok(())
607 } 693 }
608} 694}
695
696impl HirDisplay for Obligation {
697 fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
698 match self {
699 Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)),
700 Obligation::Projection(proj) => write!(
701 f,
702 "Normalize({} => {})",
703 proj.projection_ty.display(f.db),
704 proj.ty.display(f.db)
705 ),
706 }
707 }
708}
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index 594c5bc79..675df4a22 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -245,7 +245,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
245 &self.resolver, 245 &self.resolver,
246 type_ref, 246 type_ref,
247 ); 247 );
248 self.insert_type_vars(ty) 248 let ty = self.insert_type_vars(ty);
249 self.normalize_associated_types_in(ty)
249 } 250 }
250 251
251 fn unify_substs(&mut self, substs1: &Substs, substs2: &Substs, depth: usize) -> bool { 252 fn unify_substs(&mut self, substs1: &Substs, substs2: &Substs, depth: usize) -> bool {
@@ -411,6 +412,32 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
411 ty 412 ty
412 } 413 }
413 414
415 /// Recurses through the given type, normalizing associated types mentioned
416 /// in it by replacing them by type variables and registering obligations to
417 /// resolve later. This should be done once for every type we get from some
418 /// type annotation (e.g. from a let type annotation, field type or function
419 /// call). `make_ty` handles this already, but e.g. for field types we need
420 /// to do it as well.
421 fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
422 let ty = self.resolve_ty_as_possible(&mut vec![], ty);
423 ty.fold(&mut |ty| match ty {
424 Ty::Projection(proj_ty) => self.normalize_projection_ty(proj_ty),
425 Ty::UnselectedProjection(proj_ty) => {
426 // FIXME use Chalk's unselected projection support
427 Ty::UnselectedProjection(proj_ty)
428 }
429 _ => ty,
430 })
431 }
432
433 fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
434 let var = self.new_type_var();
435 let predicate = ProjectionPredicate { projection_ty: proj_ty.clone(), ty: var.clone() };
436 let obligation = Obligation::Projection(predicate);
437 self.obligations.push(obligation);
438 var
439 }
440
414 /// Resolves the type completely; type variables without known type are 441 /// Resolves the type completely; type variables without known type are
415 /// replaced by Ty::Unknown. 442 /// replaced by Ty::Unknown.
416 fn resolve_ty_completely(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty { 443 fn resolve_ty_completely(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
@@ -549,6 +576,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
549 let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); 576 let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable);
550 let ty = ty.subst(&substs); 577 let ty = ty.subst(&substs);
551 let ty = self.insert_type_vars(ty); 578 let ty = self.insert_type_vars(ty);
579 let ty = self.normalize_associated_types_in(ty);
552 Some(ty) 580 Some(ty)
553 } 581 }
554 Resolution::LocalBinding(pat) => { 582 Resolution::LocalBinding(pat) => {
@@ -670,6 +698,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
670 .and_then(|d| d.field(self.db, &Name::tuple_field_name(i))) 698 .and_then(|d| d.field(self.db, &Name::tuple_field_name(i)))
671 .map_or(Ty::Unknown, |field| field.ty(self.db)) 699 .map_or(Ty::Unknown, |field| field.ty(self.db))
672 .subst(&substs); 700 .subst(&substs);
701 let expected_ty = self.normalize_associated_types_in(expected_ty);
673 self.infer_pat(subpat, &expected_ty, default_bm); 702 self.infer_pat(subpat, &expected_ty, default_bm);
674 } 703 }
675 704
@@ -697,6 +726,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
697 let matching_field = def.and_then(|it| it.field(self.db, &subpat.name)); 726 let matching_field = def.and_then(|it| it.field(self.db, &subpat.name));
698 let expected_ty = 727 let expected_ty =
699 matching_field.map_or(Ty::Unknown, |field| field.ty(self.db)).subst(&substs); 728 matching_field.map_or(Ty::Unknown, |field| field.ty(self.db)).subst(&substs);
729 let expected_ty = self.normalize_associated_types_in(expected_ty);
700 self.infer_pat(subpat.pat, &expected_ty, default_bm); 730 self.infer_pat(subpat.pat, &expected_ty, default_bm);
701 } 731 }
702 732
@@ -927,9 +957,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
927 self.unify(&expected_receiver_ty, &actual_receiver_ty); 957 self.unify(&expected_receiver_ty, &actual_receiver_ty);
928 958
929 let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); 959 let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
930 for (arg, param) in args.iter().zip(param_iter) { 960 for (arg, param_ty) in args.iter().zip(param_iter) {
931 self.infer_expr(*arg, &Expectation::has_type(param)); 961 let param_ty = self.normalize_associated_types_in(param_ty);
962 self.infer_expr(*arg, &Expectation::has_type(param_ty));
932 } 963 }
964 let ret_ty = self.normalize_associated_types_in(ret_ty);
933 ret_ty 965 ret_ty
934 } 966 }
935 967
@@ -1020,9 +1052,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
1020 }; 1052 };
1021 self.register_obligations_for_call(&callee_ty); 1053 self.register_obligations_for_call(&callee_ty);
1022 let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); 1054 let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
1023 for (arg, param) in args.iter().zip(param_iter) { 1055 for (arg, param_ty) in args.iter().zip(param_iter) {
1024 self.infer_expr(*arg, &Expectation::has_type(param)); 1056 let param_ty = self.normalize_associated_types_in(param_ty);
1057 self.infer_expr(*arg, &Expectation::has_type(param_ty));
1025 } 1058 }
1059 let ret_ty = self.normalize_associated_types_in(ret_ty);
1026 ret_ty 1060 ret_ty
1027 } 1061 }
1028 Expr::MethodCall { receiver, args, method_name, generic_args } => self 1062 Expr::MethodCall { receiver, args, method_name, generic_args } => self
@@ -1120,7 +1154,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
1120 _ => None, 1154 _ => None,
1121 }) 1155 })
1122 .unwrap_or(Ty::Unknown); 1156 .unwrap_or(Ty::Unknown);
1123 self.insert_type_vars(ty) 1157 let ty = self.insert_type_vars(ty);
1158 self.normalize_associated_types_in(ty)
1124 } 1159 }
1125 Expr::Await { expr } => { 1160 Expr::Await { expr } => {
1126 let inner_ty = self.infer_expr(*expr, &Expectation::none()); 1161 let inner_ty = self.infer_expr(*expr, &Expectation::none());
diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs
index 894ba0695..debedcbb8 100644
--- a/crates/ra_hir/src/ty/lower.rs
+++ b/crates/ra_hir/src/ty/lower.rs
@@ -8,7 +8,7 @@
8use std::iter; 8use std::iter;
9use std::sync::Arc; 9use std::sync::Arc;
10 10
11use super::{FnSig, GenericPredicate, Substs, TraitRef, Ty, TypeCtor}; 11use super::{FnSig, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor};
12use crate::{ 12use crate::{
13 adt::VariantDef, 13 adt::VariantDef,
14 generics::HasGenericParams, 14 generics::HasGenericParams,
@@ -64,7 +64,8 @@ impl Ty {
64 64
65 pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self { 65 pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self {
66 // Resolve the path (in type namespace) 66 // Resolve the path (in type namespace)
67 let resolution = resolver.resolve_path_without_assoc_items(db, path).take_types(); 67 let (resolution, remaining_index) = resolver.resolve_path_segments(db, path).into_inner();
68 let resolution = resolution.take_types();
68 69
69 let def = match resolution { 70 let def = match resolution {
70 Some(Resolution::Def(def)) => def, 71 Some(Resolution::Def(def)) => def,
@@ -73,6 +74,10 @@ impl Ty {
73 panic!("path resolved to local binding in type ns"); 74 panic!("path resolved to local binding in type ns");
74 } 75 }
75 Some(Resolution::GenericParam(idx)) => { 76 Some(Resolution::GenericParam(idx)) => {
77 if remaining_index.is_some() {
78 // e.g. T::Item
79 return Ty::Unknown;
80 }
76 return Ty::Param { 81 return Ty::Param {
77 idx, 82 idx,
78 // FIXME: maybe return name in resolution? 83 // FIXME: maybe return name in resolution?
@@ -83,18 +88,54 @@ impl Ty {
83 }; 88 };
84 } 89 }
85 Some(Resolution::SelfType(impl_block)) => { 90 Some(Resolution::SelfType(impl_block)) => {
91 if remaining_index.is_some() {
92 // e.g. Self::Item
93 return Ty::Unknown;
94 }
86 return impl_block.target_ty(db); 95 return impl_block.target_ty(db);
87 } 96 }
88 None => return Ty::Unknown, 97 None => {
98 // path did not resolve
99 return Ty::Unknown;
100 }
89 }; 101 };
90 102
91 let typable: TypableDef = match def.into() { 103 if let ModuleDef::Trait(trait_) = def {
92 None => return Ty::Unknown, 104 let segment = match remaining_index {
93 Some(it) => it, 105 None => path.segments.last().expect("resolved path has at least one element"),
94 }; 106 Some(i) => &path.segments[i - 1],
95 let ty = db.type_for_def(typable, Namespace::Types); 107 };
96 let substs = Ty::substs_from_path(db, resolver, path, typable); 108 let trait_ref = TraitRef::from_resolved_path(db, resolver, trait_, segment, None);
97 ty.subst(&substs) 109 if let Some(remaining_index) = remaining_index {
110 if remaining_index == path.segments.len() - 1 {
111 let segment = &path.segments[remaining_index];
112 let associated_ty =
113 match trait_ref.trait_.associated_type_by_name(db, segment.name.clone()) {
114 Some(t) => t,
115 None => {
116 // associated type not found
117 return Ty::Unknown;
118 }
119 };
120 // FIXME handle type parameters on the segment
121 Ty::Projection(ProjectionTy { associated_ty, parameters: trait_ref.substs })
122 } else {
123 // FIXME more than one segment remaining, is this possible?
124 Ty::Unknown
125 }
126 } else {
127 // FIXME dyn Trait without the dyn
128 Ty::Unknown
129 }
130 } else {
131 let typable: TypableDef = match def.into() {
132 None => return Ty::Unknown,
133 Some(it) => it,
134 };
135 let ty = db.type_for_def(typable, Namespace::Types);
136 let substs = Ty::substs_from_path(db, resolver, path, typable);
137 ty.subst(&substs)
138 }
98 } 139 }
99 140
100 pub(super) fn substs_from_path_segment( 141 pub(super) fn substs_from_path_segment(
@@ -219,14 +260,25 @@ impl TraitRef {
219 Resolution::Def(ModuleDef::Trait(tr)) => tr, 260 Resolution::Def(ModuleDef::Trait(tr)) => tr,
220 _ => return None, 261 _ => return None,
221 }; 262 };
222 let mut substs = Self::substs_from_path(db, resolver, path, resolved); 263 let segment = path.segments.last().expect("path should have at least one segment");
264 Some(TraitRef::from_resolved_path(db, resolver, resolved, segment, explicit_self_ty))
265 }
266
267 fn from_resolved_path(
268 db: &impl HirDatabase,
269 resolver: &Resolver,
270 resolved: Trait,
271 segment: &PathSegment,
272 explicit_self_ty: Option<Ty>,
273 ) -> Self {
274 let mut substs = TraitRef::substs_from_path(db, resolver, segment, resolved);
223 if let Some(self_ty) = explicit_self_ty { 275 if let Some(self_ty) = explicit_self_ty {
224 // FIXME this could be nicer 276 // FIXME this could be nicer
225 let mut substs_vec = substs.0.to_vec(); 277 let mut substs_vec = substs.0.to_vec();
226 substs_vec[0] = self_ty; 278 substs_vec[0] = self_ty;
227 substs.0 = substs_vec.into(); 279 substs.0 = substs_vec.into();
228 } 280 }
229 Some(TraitRef { trait_: resolved, substs }) 281 TraitRef { trait_: resolved, substs }
230 } 282 }
231 283
232 pub(crate) fn from_hir( 284 pub(crate) fn from_hir(
@@ -245,11 +297,12 @@ impl TraitRef {
245 fn substs_from_path( 297 fn substs_from_path(
246 db: &impl HirDatabase, 298 db: &impl HirDatabase,
247 resolver: &Resolver, 299 resolver: &Resolver,
248 path: &Path, 300 segment: &PathSegment,
249 resolved: Trait, 301 resolved: Trait,
250 ) -> Substs { 302 ) -> Substs {
251 let segment = path.segments.last().expect("path should have at least one segment"); 303 let has_self_param =
252 substs_from_path_segment(db, resolver, segment, Some(resolved.into()), true) 304 segment.args_and_bindings.as_ref().map(|a| a.has_self_type).unwrap_or(false);
305 substs_from_path_segment(db, resolver, segment, Some(resolved.into()), !has_self_param)
253 } 306 }
254 307
255 pub(crate) fn for_trait(db: &impl HirDatabase, trait_: Trait) -> TraitRef { 308 pub(crate) fn for_trait(db: &impl HirDatabase, trait_: Trait) -> TraitRef {
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs
index d421bf9ef..88d012a74 100644
--- a/crates/ra_hir/src/ty/method_resolution.rs
+++ b/crates/ra_hir/src/ty/method_resolution.rs
@@ -255,6 +255,20 @@ fn iterate_inherent_methods<T>(
255 None 255 None
256} 256}
257 257
258pub(crate) fn implements_trait(
259 ty: &Canonical<Ty>,
260 db: &impl HirDatabase,
261 resolver: &Resolver,
262 krate: Crate,
263 trait_: Trait,
264) -> bool {
265 let env = lower::trait_env(db, resolver);
266 let goal = generic_implements_goal(db, env.clone(), trait_, ty.clone());
267 let solution = db.trait_solve(krate, goal);
268
269 solution.is_some()
270}
271
258impl Ty { 272impl Ty {
259 // This would be nicer if it just returned an iterator, but that runs into 273 // This would be nicer if it just returned an iterator, but that runs into
260 // lifetime problems, because we need to borrow temp `CrateImplBlocks`. 274 // lifetime problems, because we need to borrow temp `CrateImplBlocks`.
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 36dea17a3..28727bb18 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -145,6 +145,26 @@ mod collections {
145} 145}
146 146
147#[test] 147#[test]
148fn infer_while_let() {
149 covers!(infer_while_let);
150 let (db, pos) = MockDatabase::with_position(
151 r#"
152//- /main.rs
153enum Option<T> { Some(T), None }
154
155fn test() {
156 let foo: Option<f32> = None;
157 while let Option::Some(x) = foo {
158 <|>x
159 }
160}
161
162"#,
163 );
164 assert_eq!("f32", type_at_pos(&db, pos));
165}
166
167#[test]
148fn infer_basics() { 168fn infer_basics() {
149 assert_snapshot_matches!( 169 assert_snapshot_matches!(
150 infer(r#" 170 infer(r#"
@@ -2488,15 +2508,55 @@ struct S;
2488impl Iterable for S { type Item = u32; } 2508impl Iterable for S { type Item = u32; }
2489fn test<T: Iterable>() { 2509fn test<T: Iterable>() {
2490 let x: <S as Iterable>::Item = 1; 2510 let x: <S as Iterable>::Item = 1;
2491 let y: T::Item = no_matter; 2511 let y: <T as Iterable>::Item = no_matter;
2512 let z: T::Item = no_matter;
2513}
2514"#),
2515 @r###"
2516
2517 ⋮[108; 227) '{ ...ter; }': ()
2518 ⋮[118; 119) 'x': u32
2519 ⋮[145; 146) '1': u32
2520 ⋮[156; 157) 'y': {unknown}
2521 ⋮[183; 192) 'no_matter': {unknown}
2522 ⋮[202; 203) 'z': {unknown}
2523 ⋮[215; 224) 'no_matter': {unknown}
2524 "###
2525 );
2526}
2527
2528#[test]
2529fn infer_return_associated_type() {
2530 assert_snapshot_matches!(
2531 infer(r#"
2532trait Iterable {
2533 type Item;
2534}
2535struct S;
2536impl Iterable for S { type Item = u32; }
2537fn foo1<T: Iterable>(t: T) -> T::Item {}
2538fn foo2<T: Iterable>(t: T) -> <T as Iterable>::Item {}
2539fn test() {
2540 let x = foo1(S);
2541 let y = foo2(S);
2492} 2542}
2493"#), 2543"#),
2494 @r###" 2544 @r###"
2495[108; 181) '{ ...ter; }': () 2545
2496[118; 119) 'x': i32 2546 ⋮[106; 107) 't': T
2497[145; 146) '1': i32 2547 ⋮[123; 125) '{}': ()
2498[156; 157) 'y': {unknown} 2548 ⋮[147; 148) 't': T
2499[169; 178) 'no_matter': {unknown}"### 2549 ⋮[178; 180) '{}': ()
2550 ⋮[191; 236) '{ ...(S); }': ()
2551 ⋮[201; 202) 'x': {unknown}
2552 ⋮[205; 209) 'foo1': fn foo1<S>(T) -> {unknown}
2553 ⋮[205; 212) 'foo1(S)': {unknown}
2554 ⋮[210; 211) 'S': S
2555 ⋮[222; 223) 'y': u32
2556 ⋮[226; 230) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item
2557 ⋮[226; 233) 'foo2(S)': u32
2558 ⋮[231; 232) 'S': S
2559 "###
2500 ); 2560 );
2501} 2561}
2502 2562
@@ -3121,6 +3181,55 @@ fn test<T: Trait>(t: T) { (*t)<|>; }
3121 assert_eq!(t, "i128"); 3181 assert_eq!(t, "i128");
3122} 3182}
3123 3183
3184#[test]
3185fn associated_type_placeholder() {
3186 let t = type_at(
3187 r#"
3188//- /main.rs
3189pub trait ApplyL {
3190 type Out;
3191}
3192
3193pub struct RefMutL<T>;
3194
3195impl<T> ApplyL for RefMutL<T> {
3196 type Out = <T as ApplyL>::Out;
3197}
3198
3199fn test<T: ApplyL>() {
3200 let y: <RefMutL<T> as ApplyL>::Out = no_matter;
3201 y<|>;
3202}
3203"#,
3204 );
3205 // inside the generic function, the associated type gets normalized to a placeholder `ApplL::Out<T>` [https://rust-lang.github.io/rustc-guide/traits/associated-types.html#placeholder-associated-types].
3206 // FIXME: fix type parameter names going missing when going through Chalk
3207 assert_eq!(t, "ApplyL::Out<[missing name]>");
3208}
3209
3210#[test]
3211fn associated_type_placeholder_2() {
3212 let t = type_at(
3213 r#"
3214//- /main.rs
3215pub trait ApplyL {
3216 type Out;
3217}
3218fn foo<T: ApplyL>(t: T) -> <T as ApplyL>::Out;
3219
3220fn test<T: ApplyL>(t: T) {
3221 let y = foo(t);
3222 y<|>;
3223}
3224"#,
3225 );
3226 // FIXME here Chalk doesn't normalize the type to a placeholder. I think we
3227 // need to add a rule like Normalize(<T as ApplyL>::Out -> ApplyL::Out<T>)
3228 // to the trait env ourselves here; probably Chalk can't do this by itself.
3229 // assert_eq!(t, "ApplyL::Out<[missing name]>");
3230 assert_eq!(t, "{unknown}");
3231}
3232
3124fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { 3233fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
3125 let file = db.parse(pos.file_id).ok().unwrap(); 3234 let file = db.parse(pos.file_id).ok().unwrap();
3126 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); 3235 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs
index 0769e6e17..fde5d8a47 100644
--- a/crates/ra_hir/src/ty/traits.rs
+++ b/crates/ra_hir/src/ty/traits.rs
@@ -7,7 +7,7 @@ use parking_lot::Mutex;
7use ra_prof::profile; 7use ra_prof::profile;
8use rustc_hash::FxHashSet; 8use rustc_hash::FxHashSet;
9 9
10use super::{Canonical, GenericPredicate, ProjectionTy, TraitRef, Ty}; 10use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty};
11use crate::{db::HirDatabase, Crate, ImplBlock, Trait}; 11use crate::{db::HirDatabase, Crate, ImplBlock, Trait};
12 12
13use self::chalk::{from_chalk, ToChalk}; 13use self::chalk::{from_chalk, ToChalk};
@@ -61,7 +61,6 @@ fn solve(
61) -> Option<chalk_solve::Solution> { 61) -> Option<chalk_solve::Solution> {
62 let context = ChalkContext { db, krate }; 62 let context = ChalkContext { db, krate };
63 let solver = db.trait_solver(krate); 63 let solver = db.trait_solver(krate);
64 debug!("solve goal: {:?}", goal);
65 let solution = solver.lock().solve(&context, goal); 64 let solution = solver.lock().solve(&context, goal);
66 debug!("solve({:?}) => {:?}", goal, solution); 65 debug!("solve({:?}) => {:?}", goal, solution);
67 solution 66 solution
@@ -120,10 +119,11 @@ pub struct ProjectionPredicate {
120pub(crate) fn trait_solve_query( 119pub(crate) fn trait_solve_query(
121 db: &impl HirDatabase, 120 db: &impl HirDatabase,
122 krate: Crate, 121 krate: Crate,
123 trait_ref: Canonical<InEnvironment<Obligation>>, 122 goal: Canonical<InEnvironment<Obligation>>,
124) -> Option<Solution> { 123) -> Option<Solution> {
125 let _p = profile("trait_solve_query"); 124 let _p = profile("trait_solve_query");
126 let canonical = trait_ref.to_chalk(db).cast(); 125 debug!("trait_solve_query({})", goal.value.value.display(db));
126 let canonical = goal.to_chalk(db).cast();
127 // We currently don't deal with universes (I think / hope they're not yet 127 // We currently don't deal with universes (I think / hope they're not yet
128 // relevant for our use cases?) 128 // relevant for our use cases?)
129 let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 }; 129 let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs
index 9e7ae0724..6df7094c5 100644
--- a/crates/ra_hir/src/ty/traits/chalk.rs
+++ b/crates/ra_hir/src/ty/traits/chalk.rs
@@ -45,11 +45,33 @@ impl ToChalk for Ty {
45 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty { 45 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty {
46 match self { 46 match self {
47 Ty::Apply(apply_ty) => { 47 Ty::Apply(apply_ty) => {
48 let struct_id = apply_ty.ctor.to_chalk(db); 48 let name = match apply_ty.ctor {
49 let name = TypeName::TypeKindId(struct_id.into()); 49 TypeCtor::AssociatedType(type_alias) => {
50 let type_id = type_alias.to_chalk(db);
51 TypeName::AssociatedType(type_id)
52 }
53 _ => {
54 // other TypeCtors get interned and turned into a chalk StructId
55 let struct_id = apply_ty.ctor.to_chalk(db);
56 TypeName::TypeKindId(struct_id.into())
57 }
58 };
50 let parameters = apply_ty.parameters.to_chalk(db); 59 let parameters = apply_ty.parameters.to_chalk(db);
51 chalk_ir::ApplicationTy { name, parameters }.cast() 60 chalk_ir::ApplicationTy { name, parameters }.cast()
52 } 61 }
62 Ty::Projection(proj_ty) => {
63 let associated_ty_id = proj_ty.associated_ty.to_chalk(db);
64 let parameters = proj_ty.parameters.to_chalk(db);
65 chalk_ir::ProjectionTy { associated_ty_id, parameters }.cast()
66 }
67 Ty::UnselectedProjection(proj_ty) => {
68 let type_name = lalrpop_intern::intern(&proj_ty.type_name.to_string());
69 let parameters = proj_ty.parameters.to_chalk(db);
70 chalk_ir::Ty::UnselectedProjection(chalk_ir::UnselectedProjectionTy {
71 type_name,
72 parameters,
73 })
74 }
53 Ty::Param { idx, .. } => { 75 Ty::Param { idx, .. } => {
54 PlaceholderIndex { ui: UniverseIndex::ROOT, idx: idx as usize }.to_ty() 76 PlaceholderIndex { ui: UniverseIndex::ROOT, idx: idx as usize }.to_ty()
55 } 77 }
@@ -66,15 +88,21 @@ impl ToChalk for Ty {
66 fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty) -> Self { 88 fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty) -> Self {
67 match chalk { 89 match chalk {
68 chalk_ir::Ty::Apply(apply_ty) => { 90 chalk_ir::Ty::Apply(apply_ty) => {
91 // FIXME this is kind of hacky due to the fact that
92 // TypeName::Placeholder is a Ty::Param on our side
69 match apply_ty.name { 93 match apply_ty.name {
70 TypeName::TypeKindId(TypeKindId::StructId(struct_id)) => { 94 TypeName::TypeKindId(TypeKindId::StructId(struct_id)) => {
71 let ctor = from_chalk(db, struct_id); 95 let ctor = from_chalk(db, struct_id);
72 let parameters = from_chalk(db, apply_ty.parameters); 96 let parameters = from_chalk(db, apply_ty.parameters);
73 Ty::Apply(ApplicationTy { ctor, parameters }) 97 Ty::Apply(ApplicationTy { ctor, parameters })
74 } 98 }
99 TypeName::AssociatedType(type_id) => {
100 let ctor = TypeCtor::AssociatedType(from_chalk(db, type_id));
101 let parameters = from_chalk(db, apply_ty.parameters);
102 Ty::Apply(ApplicationTy { ctor, parameters })
103 }
75 // FIXME handle TypeKindId::Trait/Type here 104 // FIXME handle TypeKindId::Trait/Type here
76 TypeName::TypeKindId(_) => unimplemented!(), 105 TypeName::TypeKindId(_) => unimplemented!(),
77 TypeName::AssociatedType(_) => unimplemented!(),
78 TypeName::Placeholder(idx) => { 106 TypeName::Placeholder(idx) => {
79 assert_eq!(idx.ui, UniverseIndex::ROOT); 107 assert_eq!(idx.ui, UniverseIndex::ROOT);
80 Ty::Param { idx: idx.idx as u32, name: crate::Name::missing() } 108 Ty::Param { idx: idx.idx as u32, name: crate::Name::missing() }
@@ -389,11 +417,12 @@ where
389 &self, 417 &self,
390 projection: &'p chalk_ir::ProjectionTy, 418 projection: &'p chalk_ir::ProjectionTy,
391 ) -> (Arc<AssociatedTyDatum>, &'p [Parameter], &'p [Parameter]) { 419 ) -> (Arc<AssociatedTyDatum>, &'p [Parameter], &'p [Parameter]) {
392 debug!("split_projection {:?}", projection); 420 let proj_ty: ProjectionTy = from_chalk(self.db, projection.clone());
393 unimplemented!() 421 debug!("split_projection {:?} = {}", projection, proj_ty.display(self.db));
422 // we don't support GATs, so I think this should always be correct currently
423 (self.db.associated_ty_data(projection.associated_ty_id), &projection.parameters, &[])
394 } 424 }
395 fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause> { 425 fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause> {
396 debug!("custom_clauses");
397 vec![] 426 vec![]
398 } 427 }
399 fn all_structs(&self) -> Vec<chalk_ir::StructId> { 428 fn all_structs(&self) -> Vec<chalk_ir::StructId> {
@@ -529,6 +558,16 @@ pub(crate) fn struct_datum_query(
529 adt.krate(db) != Some(krate), 558 adt.krate(db) != Some(krate),
530 ) 559 )
531 } 560 }
561 TypeCtor::AssociatedType(type_alias) => {
562 let generic_params = type_alias.generic_params(db);
563 let bound_vars = Substs::bound_vars(&generic_params);
564 let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars);
565 (
566 generic_params.count_params_including_parent(),
567 where_clauses,
568 type_alias.krate(db) != Some(krate),
569 )
570 }
532 }; 571 };
533 let flags = chalk_rust_ir::StructFlags { 572 let flags = chalk_rust_ir::StructFlags {
534 upstream, 573 upstream,
diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml
index dd11ec0f6..a7dc0b63a 100644
--- a/crates/ra_ide_api/Cargo.toml
+++ b/crates/ra_ide_api/Cargo.toml
@@ -27,7 +27,7 @@ test_utils = { path = "../test_utils" }
27ra_assists = { path = "../ra_assists" } 27ra_assists = { path = "../ra_assists" }
28 28
29[dev-dependencies] 29[dev-dependencies]
30insta = "0.9.0" 30insta = "0.10.0"
31 31
32[dev-dependencies.proptest] 32[dev-dependencies.proptest]
33version = "0.9.0" 33version = "0.9.0"
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs
index 536ba36df..d43ff2eec 100644
--- a/crates/ra_ide_api/src/completion/complete_dot.rs
+++ b/crates/ra_ide_api/src/completion/complete_dot.rs
@@ -1,19 +1,36 @@
1use hir::{AdtDef, Ty, TypeCtor}; 1use hir::{AdtDef, Ty, TypeCtor};
2 2
3use crate::completion::{CompletionContext, Completions}; 3use crate::completion::completion_item::CompletionKind;
4use crate::{
5 completion::{completion_context::CompletionContext, completion_item::Completions},
6 CompletionItem,
7};
4use rustc_hash::FxHashSet; 8use rustc_hash::FxHashSet;
5 9
6/// Complete dot accesses, i.e. fields or methods (currently only fields). 10/// Complete dot accesses, i.e. fields or methods (and .await syntax).
7pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 11pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
8 let receiver_ty = 12 let dot_receiver = match &ctx.dot_receiver {
9 match ctx.dot_receiver.as_ref().and_then(|it| ctx.analyzer.type_of(ctx.db, it)) { 13 Some(expr) => expr,
10 Some(it) => it, 14 _ => return,
11 None => return, 15 };
12 }; 16
17 let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) {
18 Some(ty) => ty,
19 _ => return,
20 };
21
13 if !ctx.is_call { 22 if !ctx.is_call {
14 complete_fields(acc, ctx, receiver_ty.clone()); 23 complete_fields(acc, ctx, receiver_ty.clone());
15 } 24 }
16 complete_methods(acc, ctx, receiver_ty); 25 complete_methods(acc, ctx, receiver_ty.clone());
26
27 // Suggest .await syntax for types that implement Future trait
28 if ctx.analyzer.impls_future(ctx.db, receiver_ty) {
29 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
30 .detail("expr.await")
31 .insert_text("await")
32 .add_to(acc);
33 }
17} 34}
18 35
19fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { 36fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
@@ -406,4 +423,36 @@ mod tests {
406 "### 423 "###
407 ); 424 );
408 } 425 }
426
427 #[test]
428 fn test_completion_await_impls_future() {
429 assert_debug_snapshot_matches!(
430 do_completion(
431 r###"
432 //- /main.rs
433 use std::future::*;
434 struct A {}
435 impl Future for A {}
436 fn foo(a: A) {
437 a.<|>
438 }
439
440 //- /std/lib.rs
441 pub mod future {
442 pub trait Future {}
443 }
444 "###, CompletionKind::Keyword),
445 @r###"
446 ⋮[
447 ⋮ CompletionItem {
448 ⋮ label: "await",
449 ⋮ source_range: [74; 74),
450 ⋮ delete: [74; 74),
451 ⋮ insert: "await",
452 ⋮ detail: "expr.await",
453 ⋮ },
454 ⋮]
455 "###
456 )
457 }
409} 458}
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs
index 028dc3d4f..98b840b26 100644
--- a/crates/ra_ide_api/src/diagnostics.rs
+++ b/crates/ra_ide_api/src/diagnostics.rs
@@ -48,7 +48,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
48 }) 48 })
49 }) 49 })
50 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 50 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
51 let source_root = db.file_source_root(d.file().original_file(db)); 51 let source_root = db.file_source_root(d.source().file_id.original_file(db));
52 let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; 52 let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() };
53 let fix = SourceChange::file_system_edit("create module", create_file); 53 let fix = SourceChange::file_system_edit("create module", create_file);
54 res.borrow_mut().push(Diagnostic { 54 res.borrow_mut().push(Diagnostic {
diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs
index a524e014f..0b3c96d26 100644
--- a/crates/ra_ide_api/src/inlay_hints.rs
+++ b/crates/ra_ide_api/src/inlay_hints.rs
@@ -9,14 +9,9 @@ use ra_syntax::{
9 SmolStr, SyntaxKind, SyntaxNode, TextRange, 9 SmolStr, SyntaxKind, SyntaxNode, TextRange,
10}; 10};
11 11
12#[derive(Debug, PartialEq, Eq, Clone)] 12#[derive(Debug, PartialEq, Eq)]
13pub enum InlayKind { 13pub enum InlayKind {
14 LetBindingType, 14 TypeHint,
15 ClosureParameterType,
16 ForExpressionBindingType,
17 IfExpressionType,
18 WhileLetExpressionType,
19 MatchArmType,
20} 15}
21 16
22#[derive(Debug)] 17#[derive(Debug)]
@@ -46,7 +41,7 @@ fn get_inlay_hints(
46 } 41 }
47 let pat = let_statement.pat()?; 42 let pat = let_statement.pat()?;
48 let analyzer = SourceAnalyzer::new(db, file_id, let_statement.syntax(), None); 43 let analyzer = SourceAnalyzer::new(db, file_id, let_statement.syntax(), None);
49 Some(get_pat_hints(db, &analyzer, pat, InlayKind::LetBindingType, false)) 44 Some(get_pat_type_hints(db, &analyzer, pat, false))
50 }) 45 })
51 .visit(|closure_parameter: LambdaExpr| { 46 .visit(|closure_parameter: LambdaExpr| {
52 let analyzer = SourceAnalyzer::new(db, file_id, closure_parameter.syntax(), None); 47 let analyzer = SourceAnalyzer::new(db, file_id, closure_parameter.syntax(), None);
@@ -55,15 +50,7 @@ fn get_inlay_hints(
55 .params() 50 .params()
56 .filter(|closure_param| closure_param.ascribed_type().is_none()) 51 .filter(|closure_param| closure_param.ascribed_type().is_none())
57 .filter_map(|closure_param| closure_param.pat()) 52 .filter_map(|closure_param| closure_param.pat())
58 .map(|root_pat| { 53 .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, false))
59 get_pat_hints(
60 db,
61 &analyzer,
62 root_pat,
63 InlayKind::ClosureParameterType,
64 false,
65 )
66 })
67 .flatten() 54 .flatten()
68 .collect() 55 .collect()
69 }) 56 })
@@ -71,17 +58,17 @@ fn get_inlay_hints(
71 .visit(|for_expression: ForExpr| { 58 .visit(|for_expression: ForExpr| {
72 let pat = for_expression.pat()?; 59 let pat = for_expression.pat()?;
73 let analyzer = SourceAnalyzer::new(db, file_id, for_expression.syntax(), None); 60 let analyzer = SourceAnalyzer::new(db, file_id, for_expression.syntax(), None);
74 Some(get_pat_hints(db, &analyzer, pat, InlayKind::ForExpressionBindingType, false)) 61 Some(get_pat_type_hints(db, &analyzer, pat, false))
75 }) 62 })
76 .visit(|if_expr: IfExpr| { 63 .visit(|if_expr: IfExpr| {
77 let pat = if_expr.condition()?.pat()?; 64 let pat = if_expr.condition()?.pat()?;
78 let analyzer = SourceAnalyzer::new(db, file_id, if_expr.syntax(), None); 65 let analyzer = SourceAnalyzer::new(db, file_id, if_expr.syntax(), None);
79 Some(get_pat_hints(db, &analyzer, pat, InlayKind::IfExpressionType, true)) 66 Some(get_pat_type_hints(db, &analyzer, pat, true))
80 }) 67 })
81 .visit(|while_expr: WhileExpr| { 68 .visit(|while_expr: WhileExpr| {
82 let pat = while_expr.condition()?.pat()?; 69 let pat = while_expr.condition()?.pat()?;
83 let analyzer = SourceAnalyzer::new(db, file_id, while_expr.syntax(), None); 70 let analyzer = SourceAnalyzer::new(db, file_id, while_expr.syntax(), None);
84 Some(get_pat_hints(db, &analyzer, pat, InlayKind::WhileLetExpressionType, true)) 71 Some(get_pat_type_hints(db, &analyzer, pat, true))
85 }) 72 })
86 .visit(|match_arm_list: MatchArmList| { 73 .visit(|match_arm_list: MatchArmList| {
87 let analyzer = SourceAnalyzer::new(db, file_id, match_arm_list.syntax(), None); 74 let analyzer = SourceAnalyzer::new(db, file_id, match_arm_list.syntax(), None);
@@ -90,9 +77,7 @@ fn get_inlay_hints(
90 .arms() 77 .arms()
91 .map(|match_arm| match_arm.pats()) 78 .map(|match_arm| match_arm.pats())
92 .flatten() 79 .flatten()
93 .map(|root_pat| { 80 .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, true))
94 get_pat_hints(db, &analyzer, root_pat, InlayKind::MatchArmType, true)
95 })
96 .flatten() 81 .flatten()
97 .collect(), 82 .collect(),
98 ) 83 )
@@ -100,11 +85,10 @@ fn get_inlay_hints(
100 .accept(&node)? 85 .accept(&node)?
101} 86}
102 87
103fn get_pat_hints( 88fn get_pat_type_hints(
104 db: &RootDatabase, 89 db: &RootDatabase,
105 analyzer: &SourceAnalyzer, 90 analyzer: &SourceAnalyzer,
106 root_pat: Pat, 91 root_pat: Pat,
107 kind: InlayKind,
108 skip_root_pat_hint: bool, 92 skip_root_pat_hint: bool,
109) -> Vec<InlayHint> { 93) -> Vec<InlayHint> {
110 let original_pat = &root_pat.clone(); 94 let original_pat = &root_pat.clone();
@@ -118,7 +102,7 @@ fn get_pat_hints(
118 }) 102 })
119 .map(|(range, pat_type)| InlayHint { 103 .map(|(range, pat_type)| InlayHint {
120 range, 104 range,
121 kind: kind.clone(), 105 kind: InlayKind::TypeHint,
122 label: pat_type.display(db).to_string().into(), 106 label: pat_type.display(db).to_string().into(),
123 }) 107 })
124 .collect() 108 .collect()
@@ -232,52 +216,52 @@ fn main() {
232 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ 216 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[
233 InlayHint { 217 InlayHint {
234 range: [193; 197), 218 range: [193; 197),
235 kind: LetBindingType, 219 kind: TypeHint,
236 label: "i32", 220 label: "i32",
237 }, 221 },
238 InlayHint { 222 InlayHint {
239 range: [236; 244), 223 range: [236; 244),
240 kind: LetBindingType, 224 kind: TypeHint,
241 label: "i32", 225 label: "i32",
242 }, 226 },
243 InlayHint { 227 InlayHint {
244 range: [275; 279), 228 range: [275; 279),
245 kind: LetBindingType, 229 kind: TypeHint,
246 label: "&str", 230 label: "&str",
247 }, 231 },
248 InlayHint { 232 InlayHint {
249 range: [539; 543), 233 range: [539; 543),
250 kind: LetBindingType, 234 kind: TypeHint,
251 label: "(i32, char)", 235 label: "(i32, char)",
252 }, 236 },
253 InlayHint { 237 InlayHint {
254 range: [566; 567), 238 range: [566; 567),
255 kind: LetBindingType, 239 kind: TypeHint,
256 label: "i32", 240 label: "i32",
257 }, 241 },
258 InlayHint { 242 InlayHint {
259 range: [570; 571), 243 range: [570; 571),
260 kind: LetBindingType, 244 kind: TypeHint,
261 label: "i32", 245 label: "i32",
262 }, 246 },
263 InlayHint { 247 InlayHint {
264 range: [573; 574), 248 range: [573; 574),
265 kind: LetBindingType, 249 kind: TypeHint,
266 label: "i32", 250 label: "i32",
267 }, 251 },
268 InlayHint { 252 InlayHint {
269 range: [584; 585), 253 range: [584; 585),
270 kind: LetBindingType, 254 kind: TypeHint,
271 label: "i32", 255 label: "i32",
272 }, 256 },
273 InlayHint { 257 InlayHint {
274 range: [577; 578), 258 range: [577; 578),
275 kind: LetBindingType, 259 kind: TypeHint,
276 label: "f64", 260 label: "f64",
277 }, 261 },
278 InlayHint { 262 InlayHint {
279 range: [580; 581), 263 range: [580; 581),
280 kind: LetBindingType, 264 kind: TypeHint,
281 label: "f64", 265 label: "f64",
282 }, 266 },
283]"# 267]"#
@@ -299,12 +283,12 @@ fn main() {
299 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ 283 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[
300 InlayHint { 284 InlayHint {
301 range: [21; 30), 285 range: [21; 30),
302 kind: LetBindingType, 286 kind: TypeHint,
303 label: "i32", 287 label: "i32",
304 }, 288 },
305 InlayHint { 289 InlayHint {
306 range: [57; 66), 290 range: [57; 66),
307 kind: ClosureParameterType, 291 kind: TypeHint,
308 label: "i32", 292 label: "i32",
309 }, 293 },
310]"# 294]"#
@@ -326,12 +310,12 @@ fn main() {
326 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ 310 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[
327 InlayHint { 311 InlayHint {
328 range: [21; 30), 312 range: [21; 30),
329 kind: LetBindingType, 313 kind: TypeHint,
330 label: "i32", 314 label: "i32",
331 }, 315 },
332 InlayHint { 316 InlayHint {
333 range: [44; 53), 317 range: [44; 53),
334 kind: ForExpressionBindingType, 318 kind: TypeHint,
335 label: "i32", 319 label: "i32",
336 }, 320 },
337]"# 321]"#
@@ -364,7 +348,7 @@ fn main() {
364 if let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {}; 348 if let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {};
365 if let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {}; 349 if let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {};
366 if let CustomOption::Some(Test { b: y, .. }) = &test {}; 350 if let CustomOption::Some(Test { b: y, .. }) = &test {};
367 351
368 if test == CustomOption::None {} 352 if test == CustomOption::None {}
369}"#, 353}"#,
370 ); 354 );
@@ -372,27 +356,27 @@ fn main() {
372 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ 356 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[
373 InlayHint { 357 InlayHint {
374 range: [166; 170), 358 range: [166; 170),
375 kind: LetBindingType, 359 kind: TypeHint,
376 label: "CustomOption<Test>", 360 label: "CustomOption<Test>",
377 }, 361 },
378 InlayHint { 362 InlayHint {
379 range: [334; 338), 363 range: [334; 338),
380 kind: IfExpressionType, 364 kind: TypeHint,
381 label: "&Test", 365 label: "&Test",
382 }, 366 },
383 InlayHint { 367 InlayHint {
384 range: [389; 390), 368 range: [389; 390),
385 kind: IfExpressionType, 369 kind: TypeHint,
386 label: "&CustomOption<u32>", 370 label: "&CustomOption<u32>",
387 }, 371 },
388 InlayHint { 372 InlayHint {
389 range: [392; 393), 373 range: [392; 393),
390 kind: IfExpressionType, 374 kind: TypeHint,
391 label: "&u8", 375 label: "&u8",
392 }, 376 },
393 InlayHint { 377 InlayHint {
394 range: [531; 532), 378 range: [531; 532),
395 kind: IfExpressionType, 379 kind: TypeHint,
396 label: "&u32", 380 label: "&u32",
397 }, 381 },
398]"# 382]"#
@@ -425,18 +409,40 @@ fn main() {
425 while let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {}; 409 while let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {};
426 while let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {}; 410 while let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {};
427 while let CustomOption::Some(Test { b: y, .. }) = &test {}; 411 while let CustomOption::Some(Test { b: y, .. }) = &test {};
428 412
429 while test == CustomOption::None {} 413 while test == CustomOption::None {}
430}"#, 414}"#,
431 ); 415 );
432 416
433 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ 417 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r###"
434 InlayHint { 418 ⋮[
435 range: [166; 170), 419 ⋮ InlayHint {
436 kind: LetBindingType, 420 ⋮ range: [166; 170),
437 label: "CustomOption<Test>", 421 ⋮ kind: TypeHint,
438 }, 422 ⋮ label: "CustomOption<Test>",
439]"# 423 ⋮ },
424 ⋮ InlayHint {
425 ⋮ range: [343; 347),
426 ⋮ kind: TypeHint,
427 ⋮ label: "&Test",
428 ⋮ },
429 ⋮ InlayHint {
430 ⋮ range: [401; 402),
431 ⋮ kind: TypeHint,
432 ⋮ label: "&CustomOption<u32>",
433 ⋮ },
434 ⋮ InlayHint {
435 ⋮ range: [404; 405),
436 ⋮ kind: TypeHint,
437 ⋮ label: "&u8",
438 ⋮ },
439 ⋮ InlayHint {
440 ⋮ range: [549; 550),
441 ⋮ kind: TypeHint,
442 ⋮ label: "&u32",
443 ⋮ },
444 ⋮]
445 "###
440 ); 446 );
441 } 447 }
442 448
@@ -445,7 +451,7 @@ fn main() {
445 let (analysis, file_id) = single_file( 451 let (analysis, file_id) = single_file(
446 r#" 452 r#"
447#[derive(PartialEq)] 453#[derive(PartialEq)]
448enum CustomOption<T> { 454enum CustomOption<T> {
449 None, 455 None,
450 Some(T), 456 Some(T),
451} 457}
@@ -473,23 +479,23 @@ fn main() {
473 479
474 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ 480 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[
475 InlayHint { 481 InlayHint {
476 range: [312; 316), 482 range: [311; 315),
477 kind: MatchArmType, 483 kind: TypeHint,
478 label: "Test", 484 label: "Test",
479 }, 485 },
480 InlayHint { 486 InlayHint {
481 range: [359; 360), 487 range: [358; 359),
482 kind: MatchArmType, 488 kind: TypeHint,
483 label: "CustomOption<u32>", 489 label: "CustomOption<u32>",
484 }, 490 },
485 InlayHint { 491 InlayHint {
486 range: [362; 363), 492 range: [361; 362),
487 kind: MatchArmType, 493 kind: TypeHint,
488 label: "u8", 494 label: "u8",
489 }, 495 },
490 InlayHint { 496 InlayHint {
491 range: [485; 486), 497 range: [484; 485),
492 kind: MatchArmType, 498 kind: TypeHint,
493 label: "u32", 499 label: "u32",
494 }, 500 },
495]"# 501]"#
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index cec360667..c282d6db8 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -25,6 +25,7 @@ ra_ide_api = { path = "../ra_ide_api" }
25gen_lsp_server = { path = "../gen_lsp_server" } 25gen_lsp_server = { path = "../gen_lsp_server" }
26ra_project_model = { path = "../ra_project_model" } 26ra_project_model = { path = "../ra_project_model" }
27ra_prof = { path = "../ra_prof" } 27ra_prof = { path = "../ra_prof" }
28ra_vfs_glob = { path = "../ra_vfs_glob" }
28 29
29[dev-dependencies] 30[dev-dependencies]
30tempfile = "3" 31tempfile = "3"
diff --git a/crates/ra_lsp_server/src/init.rs b/crates/ra_lsp_server/src/config.rs
index b894b449d..6dcdc695a 100644
--- a/crates/ra_lsp_server/src/init.rs
+++ b/crates/ra_lsp_server/src/config.rs
@@ -1,9 +1,9 @@
1use serde::{Deserialize, Deserializer}; 1use serde::{Deserialize, Deserializer};
2 2
3/// Client provided initialization options 3/// Client provided initialization options
4#[derive(Deserialize, Clone, Copy, Debug, PartialEq, Eq)] 4#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
5#[serde(rename_all = "camelCase", default)] 5#[serde(rename_all = "camelCase", default)]
6pub struct InitializationOptions { 6pub struct ServerConfig {
7 /// Whether the client supports our custom highlighting publishing decorations. 7 /// Whether the client supports our custom highlighting publishing decorations.
8 /// This is different to the highlightingOn setting, which is whether the user 8 /// This is different to the highlightingOn setting, which is whether the user
9 /// wants our custom highlighting to be used. 9 /// wants our custom highlighting to be used.
@@ -18,14 +18,17 @@ pub struct InitializationOptions {
18 #[serde(deserialize_with = "nullable_bool_true")] 18 #[serde(deserialize_with = "nullable_bool_true")]
19 pub show_workspace_loaded: bool, 19 pub show_workspace_loaded: bool,
20 20
21 pub exclude_globs: Vec<String>,
22
21 pub lru_capacity: Option<usize>, 23 pub lru_capacity: Option<usize>,
22} 24}
23 25
24impl Default for InitializationOptions { 26impl Default for ServerConfig {
25 fn default() -> InitializationOptions { 27 fn default() -> ServerConfig {
26 InitializationOptions { 28 ServerConfig {
27 publish_decorations: false, 29 publish_decorations: false,
28 show_workspace_loaded: true, 30 show_workspace_loaded: true,
31 exclude_globs: Vec::new(),
29 lru_capacity: None, 32 lru_capacity: None,
30 } 33 }
31 } 34 }
@@ -56,7 +59,7 @@ mod test {
56 #[test] 59 #[test]
57 fn deserialize_init_options_defaults() { 60 fn deserialize_init_options_defaults() {
58 // check that null == default for both fields 61 // check that null == default for both fields
59 let default = InitializationOptions::default(); 62 let default = ServerConfig::default();
60 assert_eq!(default, serde_json::from_str(r#"{}"#).unwrap()); 63 assert_eq!(default, serde_json::from_str(r#"{}"#).unwrap());
61 assert_eq!( 64 assert_eq!(
62 default, 65 default,
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index 59c5e1582..df8ea6e0d 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -1,13 +1,13 @@
1use lsp_types::{ 1use lsp_types::{
2 self, CreateFile, DocumentChangeOperation, DocumentChanges, Documentation, Location, 2 self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation,
3 LocationLink, MarkupContent, MarkupKind, Position, Range, RenameFile, ResourceOp, SymbolKind, 3 Location, LocationLink, MarkupContent, MarkupKind, Position, Range, RenameFile, ResourceOp,
4 TextDocumentEdit, TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url, 4 SymbolKind, TextDocumentEdit, TextDocumentIdentifier, TextDocumentItem,
5 VersionedTextDocumentIdentifier, WorkspaceEdit, 5 TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkspaceEdit,
6}; 6};
7use ra_ide_api::{ 7use ra_ide_api::{
8 translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, 8 translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition,
9 FileRange, FileSystemEdit, InsertTextFormat, LineCol, LineIndex, NavigationTarget, RangeInfo, 9 FileRange, FileSystemEdit, InsertTextFormat, LineCol, LineIndex, NavigationTarget, RangeInfo,
10 SourceChange, SourceFileEdit, 10 Severity, SourceChange, SourceFileEdit,
11}; 11};
12use ra_syntax::{SyntaxKind, TextRange, TextUnit}; 12use ra_syntax::{SyntaxKind, TextRange, TextUnit};
13use ra_text_edit::{AtomTextEdit, TextEdit}; 13use ra_text_edit::{AtomTextEdit, TextEdit};
@@ -79,6 +79,16 @@ impl Conv for CompletionItemKind {
79 } 79 }
80} 80}
81 81
82impl Conv for Severity {
83 type Output = DiagnosticSeverity;
84 fn conv(self) -> DiagnosticSeverity {
85 match self {
86 Severity::Error => DiagnosticSeverity::Error,
87 Severity::WeakWarning => DiagnosticSeverity::Hint,
88 }
89 }
90}
91
82impl ConvWith for CompletionItem { 92impl ConvWith for CompletionItem {
83 type Ctx = LineIndex; 93 type Ctx = LineIndex;
84 type Output = ::lsp_types::CompletionItem; 94 type Output = ::lsp_types::CompletionItem;
diff --git a/crates/ra_lsp_server/src/lib.rs b/crates/ra_lsp_server/src/lib.rs
index 56a263aa5..795f86383 100644
--- a/crates/ra_lsp_server/src/lib.rs
+++ b/crates/ra_lsp_server/src/lib.rs
@@ -4,13 +4,11 @@ mod conv;
4mod main_loop; 4mod main_loop;
5mod markdown; 5mod markdown;
6mod project_model; 6mod project_model;
7mod vfs_filter;
8pub mod req; 7pub mod req;
9pub mod init; 8pub mod config;
10mod world; 9mod world;
11 10
12pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; 11pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
13pub use crate::{ 12pub use crate::{
14 caps::server_capabilities, init::InitializationOptions, main_loop::main_loop, 13 caps::server_capabilities, config::ServerConfig, main_loop::main_loop, main_loop::LspError,
15 main_loop::LspError,
16}; 14};
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs
index c1f8243be..1a2ab1bc2 100644
--- a/crates/ra_lsp_server/src/main.rs
+++ b/crates/ra_lsp_server/src/main.rs
@@ -2,7 +2,7 @@ use flexi_logger::{Duplicate, Logger};
2use gen_lsp_server::{run_server, stdio_transport}; 2use gen_lsp_server::{run_server, stdio_transport};
3use serde::Deserialize; 3use serde::Deserialize;
4 4
5use ra_lsp_server::{InitializationOptions, Result}; 5use ra_lsp_server::{Result, ServerConfig};
6use ra_prof; 6use ra_prof;
7 7
8fn main() -> Result<()> { 8fn main() -> Result<()> {
@@ -48,7 +48,7 @@ fn main_inner() -> Result<()> {
48 48
49 let opts = params 49 let opts = params
50 .initialization_options 50 .initialization_options
51 .and_then(|v| InitializationOptions::deserialize(v).ok()) 51 .and_then(|v| ServerConfig::deserialize(v).ok())
52 .unwrap_or_default(); 52 .unwrap_or_default();
53 53
54 ra_lsp_server::main_loop(workspace_roots, params.capabilities, opts, r, s) 54 ra_lsp_server::main_loop(workspace_roots, params.capabilities, opts, r, s)
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 8e830c8b8..b9c99a223 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -23,7 +23,7 @@ use crate::{
23 project_model::workspace_loader, 23 project_model::workspace_loader,
24 req, 24 req,
25 world::{Options, WorldSnapshot, WorldState}, 25 world::{Options, WorldSnapshot, WorldState},
26 InitializationOptions, Result, 26 Result, ServerConfig,
27}; 27};
28 28
29const THREADPOOL_SIZE: usize = 8; 29const THREADPOOL_SIZE: usize = 8;
@@ -52,10 +52,11 @@ impl Error for LspError {}
52pub fn main_loop( 52pub fn main_loop(
53 ws_roots: Vec<PathBuf>, 53 ws_roots: Vec<PathBuf>,
54 client_caps: ClientCapabilities, 54 client_caps: ClientCapabilities,
55 options: InitializationOptions, 55 config: ServerConfig,
56 msg_receiver: &Receiver<RawMessage>, 56 msg_receiver: &Receiver<RawMessage>,
57 msg_sender: &Sender<RawMessage>, 57 msg_sender: &Sender<RawMessage>,
58) -> Result<()> { 58) -> Result<()> {
59 log::debug!("server_config: {:?}", config);
59 // FIXME: support dynamic workspace loading. 60 // FIXME: support dynamic workspace loading.
60 let workspaces = { 61 let workspaces = {
61 let ws_worker = workspace_loader(); 62 let ws_worker = workspace_loader();
@@ -77,14 +78,19 @@ pub fn main_loop(
77 } 78 }
78 loaded_workspaces 79 loaded_workspaces
79 }; 80 };
80 81 let globs = config
82 .exclude_globs
83 .iter()
84 .map(|glob| ra_vfs_glob::Glob::new(glob))
85 .collect::<std::result::Result<Vec<_>, _>>()?;
81 let mut state = WorldState::new( 86 let mut state = WorldState::new(
82 ws_roots, 87 ws_roots,
83 workspaces, 88 workspaces,
84 options.lru_capacity, 89 config.lru_capacity,
90 &globs,
85 Options { 91 Options {
86 publish_decorations: options.publish_decorations, 92 publish_decorations: config.publish_decorations,
87 show_workspace_loaded: options.show_workspace_loaded, 93 show_workspace_loaded: config.show_workspace_loaded,
88 supports_location_link: client_caps 94 supports_location_link: client_caps
89 .text_document 95 .text_document
90 .and_then(|it| it.definition) 96 .and_then(|it| it.definition)
@@ -269,7 +275,7 @@ fn main_loop_inner(
269 && pending_libraries.is_empty() 275 && pending_libraries.is_empty()
270 && in_flight_libraries == 0 276 && in_flight_libraries == 0
271 { 277 {
272 let n_packages: usize = state.workspaces.iter().map(|it| it.count()).sum(); 278 let n_packages: usize = state.workspaces.iter().map(|it| it.n_packages()).sum();
273 if state.options.show_workspace_loaded { 279 if state.options.show_workspace_loaded {
274 let msg = format!("workspace loaded, {} rust packages", n_packages); 280 let msg = format!("workspace loaded, {} rust packages", n_packages);
275 show_message(req::MessageType::Info, msg, msg_sender); 281 show_message(req::MessageType::Info, msg, msg_sender);
@@ -340,7 +346,6 @@ fn on_request(
340 })? 346 })?
341 .on::<req::AnalyzerStatus>(handlers::handle_analyzer_status)? 347 .on::<req::AnalyzerStatus>(handlers::handle_analyzer_status)?
342 .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? 348 .on::<req::SyntaxTree>(handlers::handle_syntax_tree)?
343 .on::<req::ExtendSelection>(handlers::handle_extend_selection)?
344 .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)? 349 .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)?
345 .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)? 350 .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)?
346 .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)? 351 .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index c2a44ffa0..a3d3f167c 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -3,13 +3,13 @@ use std::{fmt::Write as _, io::Write as _};
3use gen_lsp_server::ErrorCode; 3use gen_lsp_server::ErrorCode;
4use lsp_types::{ 4use lsp_types::{
5 CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, 5 CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic,
6 DiagnosticSeverity, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, 6 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeKind,
7 FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, 7 FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position,
8 MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, SymbolInformation, 8 PrepareRenameResponse, Range, RenameParams, SymbolInformation, TextDocumentIdentifier,
9 TextDocumentIdentifier, TextEdit, WorkspaceEdit, 9 TextEdit, WorkspaceEdit,
10}; 10};
11use ra_ide_api::{ 11use ra_ide_api::{
12 AssistId, Cancelable, FileId, FilePosition, FileRange, FoldKind, Query, RunnableKind, Severity, 12 AssistId, FileId, FilePosition, FileRange, FoldKind, Query, Runnable, RunnableKind,
13}; 13};
14use ra_prof::profile; 14use ra_prof::profile;
15use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; 15use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit};
@@ -45,27 +45,6 @@ pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) -
45 Ok(res) 45 Ok(res)
46} 46}
47 47
48// FIXME: drop this API
49pub fn handle_extend_selection(
50 world: WorldSnapshot,
51 params: req::ExtendSelectionParams,
52) -> Result<req::ExtendSelectionResult> {
53 log::error!(
54 "extend selection is deprecated and will be removed soon,
55 use the new selection range API in LSP",
56 );
57 let file_id = params.text_document.try_conv_with(&world)?;
58 let line_index = world.analysis().file_line_index(file_id)?;
59 let selections = params
60 .selections
61 .into_iter()
62 .map_conv_with(&line_index)
63 .map(|range| FileRange { file_id, range })
64 .map(|frange| world.analysis().extend_selection(frange).map(|it| it.conv_with(&line_index)))
65 .collect::<Cancelable<Vec<_>>>()?;
66 Ok(req::ExtendSelectionResult { selections })
67}
68
69pub fn handle_selection_range( 48pub fn handle_selection_range(
70 world: WorldSnapshot, 49 world: WorldSnapshot,
71 params: req::SelectionRangeParams, 50 params: req::SelectionRangeParams,
@@ -325,27 +304,7 @@ pub fn handle_runnables(
325 continue; 304 continue;
326 } 305 }
327 } 306 }
328 307 res.push(to_lsp_runnable(&world, file_id, runnable)?);
329 let args = runnable_args(&world, file_id, &runnable.kind)?;
330
331 let r = req::Runnable {
332 range: runnable.range.conv_with(&line_index),
333 label: match &runnable.kind {
334 RunnableKind::Test { name } => format!("test {}", name),
335 RunnableKind::TestMod { path } => format!("test-mod {}", path),
336 RunnableKind::Bench { name } => format!("bench {}", name),
337 RunnableKind::Bin => "run binary".to_string(),
338 },
339 bin: "cargo".to_string(),
340 args,
341 env: {
342 let mut m = FxHashMap::default();
343 m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
344 m
345 },
346 cwd: workspace_root.map(|root| root.to_string_lossy().to_string()),
347 };
348 res.push(r);
349 } 308 }
350 let mut check_args = vec!["check".to_string()]; 309 let mut check_args = vec!["check".to_string()];
351 let label; 310 let label;
@@ -693,42 +652,27 @@ pub fn handle_code_lens(
693 let line_index = world.analysis().file_line_index(file_id)?; 652 let line_index = world.analysis().file_line_index(file_id)?;
694 653
695 let mut lenses: Vec<CodeLens> = Default::default(); 654 let mut lenses: Vec<CodeLens> = Default::default();
696 let workspace_root = world.workspace_root_for(file_id);
697 655
698 // Gather runnables 656 // Gather runnables
699 for runnable in world.analysis().runnables(file_id)? { 657 for runnable in world.analysis().runnables(file_id)? {
700 let title = match &runnable.kind { 658 let title = match &runnable.kind {
701 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => Some("▶️Run Test"), 659 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️Run Test",
702 RunnableKind::Bench { .. } => Some("Run Bench"), 660 RunnableKind::Bench { .. } => "Run Bench",
703 RunnableKind::Bin => Some("️Run"), 661 RunnableKind::Bin => "Run",
662 }
663 .to_string();
664 let r = to_lsp_runnable(&world, file_id, runnable)?;
665 let lens = CodeLens {
666 range: r.range,
667 command: Some(Command {
668 title,
669 command: "rust-analyzer.runSingle".into(),
670 arguments: Some(vec![to_value(r).unwrap()]),
671 }),
672 data: None,
704 }; 673 };
705 674
706 if let Some(title) = title { 675 lenses.push(lens);
707 let args = runnable_args(&world, file_id, &runnable.kind)?;
708 let range = runnable.range.conv_with(&line_index);
709
710 // This represents the actual command that will be run.
711 let r: req::Runnable = req::Runnable {
712 range,
713 label: Default::default(),
714 bin: "cargo".into(),
715 args,
716 env: Default::default(),
717 cwd: workspace_root.map(|root| root.to_string_lossy().to_string()),
718 };
719
720 let lens = CodeLens {
721 range,
722 command: Some(Command {
723 title: title.into(),
724 command: "rust-analyzer.runSingle".into(),
725 arguments: Some(vec![to_value(r).unwrap()]),
726 }),
727 data: None,
728 };
729
730 lenses.push(lens);
731 }
732 } 676 }
733 677
734 // Handle impls 678 // Handle impls
@@ -838,7 +782,7 @@ pub fn publish_diagnostics(
838 .into_iter() 782 .into_iter()
839 .map(|d| Diagnostic { 783 .map(|d| Diagnostic {
840 range: d.range.conv_with(&line_index), 784 range: d.range.conv_with(&line_index),
841 severity: Some(to_diagnostic_severity(d.severity)), 785 severity: Some(d.severity.conv()),
842 code: None, 786 code: None,
843 source: Some("rust-analyzer".to_string()), 787 source: Some("rust-analyzer".to_string()),
844 message: d.message, 788 message: d.message,
@@ -856,6 +800,32 @@ pub fn publish_decorations(
856 Ok(req::PublishDecorationsParams { uri, decorations: highlight(&world, file_id)? }) 800 Ok(req::PublishDecorationsParams { uri, decorations: highlight(&world, file_id)? })
857} 801}
858 802
803fn to_lsp_runnable(
804 world: &WorldSnapshot,
805 file_id: FileId,
806 runnable: Runnable,
807) -> Result<req::Runnable> {
808 let args = runnable_args(world, file_id, &runnable.kind)?;
809 let line_index = world.analysis().file_line_index(file_id)?;
810 let label = match &runnable.kind {
811 RunnableKind::Test { name } => format!("test {}", name),
812 RunnableKind::TestMod { path } => format!("test-mod {}", path),
813 RunnableKind::Bench { name } => format!("bench {}", name),
814 RunnableKind::Bin => "run binary".to_string(),
815 };
816 Ok(req::Runnable {
817 range: runnable.range.conv_with(&line_index),
818 label,
819 bin: "cargo".to_string(),
820 args,
821 env: {
822 let mut m = FxHashMap::default();
823 m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
824 m
825 },
826 cwd: world.workspace_root_for(file_id).map(|root| root.to_string_lossy().to_string()),
827 })
828}
859fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>> { 829fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>> {
860 let line_index = world.analysis().file_line_index(file_id)?; 830 let line_index = world.analysis().file_line_index(file_id)?;
861 let res = world 831 let res = world
@@ -871,15 +841,6 @@ fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>>
871 Ok(res) 841 Ok(res)
872} 842}
873 843
874fn to_diagnostic_severity(severity: Severity) -> DiagnosticSeverity {
875 use ra_ide_api::Severity::*;
876
877 match severity {
878 Error => DiagnosticSeverity::Error,
879 WeakWarning => DiagnosticSeverity::Hint,
880 }
881}
882
883pub fn handle_inlay_hints( 844pub fn handle_inlay_hints(
884 world: WorldSnapshot, 845 world: WorldSnapshot,
885 params: InlayHintsParams, 846 params: InlayHintsParams,
@@ -894,14 +855,7 @@ pub fn handle_inlay_hints(
894 label: api_type.label.to_string(), 855 label: api_type.label.to_string(),
895 range: api_type.range.conv_with(&line_index), 856 range: api_type.range.conv_with(&line_index),
896 kind: match api_type.kind { 857 kind: match api_type.kind {
897 ra_ide_api::InlayKind::LetBindingType => InlayKind::LetBindingType, 858 ra_ide_api::InlayKind::TypeHint => InlayKind::TypeHint,
898 ra_ide_api::InlayKind::ClosureParameterType => InlayKind::ClosureParameterType,
899 ra_ide_api::InlayKind::ForExpressionBindingType => {
900 InlayKind::ForExpressionBindingType
901 }
902 ra_ide_api::InlayKind::IfExpressionType => InlayKind::IfExpressionType,
903 ra_ide_api::InlayKind::WhileLetExpressionType => InlayKind::WhileLetExpressionType,
904 ra_ide_api::InlayKind::MatchArmType => InlayKind::MatchArmType,
905 }, 859 },
906 }) 860 })
907 .collect()) 861 .collect())
diff --git a/crates/ra_lsp_server/src/markdown.rs b/crates/ra_lsp_server/src/markdown.rs
index 947ef77cd..c1eb0236a 100644
--- a/crates/ra_lsp_server/src/markdown.rs
+++ b/crates/ra_lsp_server/src/markdown.rs
@@ -54,4 +54,21 @@ mod tests {
54 let comment = "this\nis\nultiline"; 54 let comment = "this\nis\nultiline";
55 assert_eq!(format_docs(comment), comment); 55 assert_eq!(format_docs(comment), comment);
56 } 56 }
57
58 #[test]
59 fn test_code_blocks_in_comments_marked_as_rust() {
60 let comment = r#"```rust
61fn main(){}
62```
63Some comment.
64```
65let a = 1;
66```"#;
67
68 assert_eq!(
69 format_docs(comment),
70 "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
71 );
72 }
73
57} 74}
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs
index 570438643..b2f3c509d 100644
--- a/crates/ra_lsp_server/src/req.rs
+++ b/crates/ra_lsp_server/src/req.rs
@@ -43,27 +43,6 @@ pub struct SyntaxTreeParams {
43 pub range: Option<Range>, 43 pub range: Option<Range>,
44} 44}
45 45
46pub enum ExtendSelection {}
47
48impl Request for ExtendSelection {
49 type Params = ExtendSelectionParams;
50 type Result = ExtendSelectionResult;
51 const METHOD: &'static str = "rust-analyzer/extendSelection";
52}
53
54#[derive(Deserialize, Debug)]
55#[serde(rename_all = "camelCase")]
56pub struct ExtendSelectionParams {
57 pub text_document: TextDocumentIdentifier,
58 pub selections: Vec<Range>,
59}
60
61#[derive(Serialize, Debug)]
62#[serde(rename_all = "camelCase")]
63pub struct ExtendSelectionResult {
64 pub selections: Vec<Range>,
65}
66
67pub enum SelectionRangeRequest {} 46pub enum SelectionRangeRequest {}
68 47
69impl Request for SelectionRangeRequest { 48impl Request for SelectionRangeRequest {
@@ -213,12 +192,7 @@ pub struct InlayHintsParams {
213 192
214#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] 193#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
215pub enum InlayKind { 194pub enum InlayKind {
216 LetBindingType, 195 TypeHint,
217 ClosureParameterType,
218 ForExpressionBindingType,
219 IfExpressionType,
220 WhileLetExpressionType,
221 MatchArmType,
222} 196}
223 197
224#[derive(Debug, Deserialize, Serialize)] 198#[derive(Debug, Deserialize, Serialize)]
diff --git a/crates/ra_lsp_server/src/vfs_filter.rs b/crates/ra_lsp_server/src/vfs_filter.rs
deleted file mode 100644
index e16a57da5..000000000
--- a/crates/ra_lsp_server/src/vfs_filter.rs
+++ /dev/null
@@ -1,54 +0,0 @@
1use ra_project_model::ProjectRoot;
2use ra_vfs::{Filter, RelativePath, RootEntry};
3use std::path::PathBuf;
4
5/// `IncludeRustFiles` is used to convert
6/// from `ProjectRoot` to `RootEntry` for VFS
7pub struct IncludeRustFiles {
8 root: ProjectRoot,
9}
10
11impl IncludeRustFiles {
12 pub fn from_roots<R>(roots: R) -> impl Iterator<Item = RootEntry>
13 where
14 R: IntoIterator<Item = ProjectRoot>,
15 {
16 roots.into_iter().map(IncludeRustFiles::from_root)
17 }
18
19 pub fn from_root(root: ProjectRoot) -> RootEntry {
20 IncludeRustFiles::from(root).into()
21 }
22
23 #[allow(unused)]
24 pub fn external(path: PathBuf) -> RootEntry {
25 IncludeRustFiles::from_root(ProjectRoot::new(path, false))
26 }
27
28 pub fn member(path: PathBuf) -> RootEntry {
29 IncludeRustFiles::from_root(ProjectRoot::new(path, true))
30 }
31}
32
33impl Filter for IncludeRustFiles {
34 fn include_dir(&self, dir_path: &RelativePath) -> bool {
35 self.root.include_dir(dir_path)
36 }
37
38 fn include_file(&self, file_path: &RelativePath) -> bool {
39 self.root.include_file(file_path)
40 }
41}
42
43impl std::convert::From<ProjectRoot> for IncludeRustFiles {
44 fn from(v: ProjectRoot) -> IncludeRustFiles {
45 IncludeRustFiles { root: v }
46 }
47}
48
49impl std::convert::From<IncludeRustFiles> for RootEntry {
50 fn from(v: IncludeRustFiles) -> RootEntry {
51 let path = v.root.path().clone();
52 RootEntry::new(path, Box::new(v))
53 }
54}
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs
index 1d7755910..9990ef62e 100644
--- a/crates/ra_lsp_server/src/world.rs
+++ b/crates/ra_lsp_server/src/world.rs
@@ -9,13 +9,13 @@ use parking_lot::RwLock;
9use ra_ide_api::{ 9use ra_ide_api::{
10 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId, 10 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId,
11}; 11};
12use ra_vfs::{Vfs, VfsChange, VfsFile, VfsRoot}; 12use ra_vfs::{RootEntry, Vfs, VfsChange, VfsFile, VfsRoot};
13use ra_vfs_glob::{Glob, RustPackageFilterBuilder};
13use relative_path::RelativePathBuf; 14use relative_path::RelativePathBuf;
14 15
15use crate::{ 16use crate::{
16 main_loop::pending_requests::{CompletedRequest, LatestRequests}, 17 main_loop::pending_requests::{CompletedRequest, LatestRequests},
17 project_model::ProjectWorkspace, 18 project_model::ProjectWorkspace,
18 vfs_filter::IncludeRustFiles,
19 LspError, Result, 19 LspError, Result,
20}; 20};
21 21
@@ -56,14 +56,28 @@ impl WorldState {
56 folder_roots: Vec<PathBuf>, 56 folder_roots: Vec<PathBuf>,
57 workspaces: Vec<ProjectWorkspace>, 57 workspaces: Vec<ProjectWorkspace>,
58 lru_capacity: Option<usize>, 58 lru_capacity: Option<usize>,
59 exclude_globs: &[Glob],
59 options: Options, 60 options: Options,
60 ) -> WorldState { 61 ) -> WorldState {
61 let mut change = AnalysisChange::new(); 62 let mut change = AnalysisChange::new();
62 63
63 let mut roots = Vec::new(); 64 let mut roots = Vec::new();
64 roots.extend(folder_roots.iter().cloned().map(IncludeRustFiles::member)); 65 roots.extend(folder_roots.iter().map(|path| {
66 let mut filter = RustPackageFilterBuilder::default().set_member(true);
67 for glob in exclude_globs.iter() {
68 filter = filter.exclude(glob.clone());
69 }
70 RootEntry::new(path.clone(), filter.into_vfs_filter())
71 }));
65 for ws in workspaces.iter() { 72 for ws in workspaces.iter() {
66 roots.extend(IncludeRustFiles::from_roots(ws.to_roots())); 73 roots.extend(ws.to_roots().into_iter().map(|pkg_root| {
74 let mut filter =
75 RustPackageFilterBuilder::default().set_member(pkg_root.is_member());
76 for glob in exclude_globs.iter() {
77 filter = filter.exclude(glob.clone());
78 }
79 RootEntry::new(pkg_root.path().clone(), filter.into_vfs_filter())
80 }));
67 } 81 }
68 82
69 let (mut vfs, vfs_roots) = Vfs::new(roots); 83 let (mut vfs, vfs_roots) = Vfs::new(roots);
@@ -211,7 +225,7 @@ impl WorldSnapshot {
211 } else { 225 } else {
212 res.push_str("workspaces:\n"); 226 res.push_str("workspaces:\n");
213 for w in self.workspaces.iter() { 227 for w in self.workspaces.iter() {
214 res += &format!("{} packages loaded\n", w.count()); 228 res += &format!("{} packages loaded\n", w.n_packages());
215 } 229 }
216 } 230 }
217 res.push_str("\nanalysis:\n"); 231 res.push_str("\nanalysis:\n");
diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs
index 5dddbbe17..ba8ee8b06 100644
--- a/crates/ra_lsp_server/tests/heavy_tests/support.rs
+++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs
@@ -22,7 +22,7 @@ use tempfile::TempDir;
22use test_utils::{find_mismatch, parse_fixture}; 22use test_utils::{find_mismatch, parse_fixture};
23use thread_worker::Worker; 23use thread_worker::Worker;
24 24
25use ra_lsp_server::{main_loop, req, InitializationOptions}; 25use ra_lsp_server::{main_loop, req, ServerConfig};
26 26
27pub struct Project<'a> { 27pub struct Project<'a> {
28 fixture: &'a str, 28 fixture: &'a str,
@@ -107,7 +107,7 @@ impl Server {
107 window: None, 107 window: None,
108 experimental: None, 108 experimental: None,
109 }, 109 },
110 InitializationOptions::default(), 110 ServerConfig::default(),
111 &msg_receiver, 111 &msg_receiver,
112 &msg_sender, 112 &msg_sender,
113 ) 113 )
diff --git a/crates/ra_mbe/src/subtree_source.rs b/crates/ra_mbe/src/subtree_source.rs
index 6603ff34d..9d6d0133f 100644
--- a/crates/ra_mbe/src/subtree_source.rs
+++ b/crates/ra_mbe/src/subtree_source.rs
@@ -1,6 +1,6 @@
1use ra_parser::{Token, TokenSource}; 1use ra_parser::{Token, TokenSource};
2use ra_syntax::{classify_literal, SmolStr, SyntaxKind, SyntaxKind::*, T}; 2use ra_syntax::{classify_literal, SmolStr, SyntaxKind, SyntaxKind::*, T};
3use std::cell::{Cell, RefCell}; 3use std::cell::{Cell, Ref, RefCell};
4use tt::buffer::{Cursor, TokenBuffer}; 4use tt::buffer::{Cursor, TokenBuffer};
5 5
6#[derive(Debug, Clone, Eq, PartialEq)] 6#[derive(Debug, Clone, Eq, PartialEq)]
@@ -20,8 +20,8 @@ impl<'a> SubtreeTokenSource<'a> {
20 // Helper function used in test 20 // Helper function used in test
21 #[cfg(test)] 21 #[cfg(test)]
22 pub fn text(&self) -> SmolStr { 22 pub fn text(&self) -> SmolStr {
23 match self.get(self.curr.1) { 23 match *self.get(self.curr.1) {
24 Some(tt) => tt.text, 24 Some(ref tt) => tt.text.clone(),
25 _ => SmolStr::new(""), 25 _ => SmolStr::new(""),
26 } 26 }
27 } 27 }
@@ -41,44 +41,46 @@ impl<'a> SubtreeTokenSource<'a> {
41 } 41 }
42 42
43 fn mk_token(&self, pos: usize) -> Token { 43 fn mk_token(&self, pos: usize) -> Token {
44 match self.get(pos) { 44 match *self.get(pos) {
45 Some(tt) => Token { kind: tt.kind, is_jointed_to_next: tt.is_joint_to_next }, 45 Some(ref tt) => Token { kind: tt.kind, is_jointed_to_next: tt.is_joint_to_next },
46 None => Token { kind: EOF, is_jointed_to_next: false }, 46 None => Token { kind: EOF, is_jointed_to_next: false },
47 } 47 }
48 } 48 }
49 49
50 fn get(&self, pos: usize) -> Option<TtToken> { 50 fn get(&self, pos: usize) -> Ref<Option<TtToken>> {
51 let mut cached = self.cached.borrow_mut(); 51 if pos < self.cached.borrow().len() {
52 if pos < cached.len() { 52 return Ref::map(self.cached.borrow(), |c| &c[pos]);
53 return cached[pos].clone();
54 } 53 }
55 54
56 while pos >= cached.len() { 55 {
57 let cursor = self.cached_cursor.get(); 56 let mut cached = self.cached.borrow_mut();
58 if cursor.eof() { 57 while pos >= cached.len() {
59 cached.push(None); 58 let cursor = self.cached_cursor.get();
60 continue; 59 if cursor.eof() {
61 } 60 cached.push(None);
62 61 continue;
63 match cursor.token_tree() {
64 Some(tt::TokenTree::Leaf(leaf)) => {
65 cached.push(Some(convert_leaf(&leaf)));
66 self.cached_cursor.set(cursor.bump());
67 }
68 Some(tt::TokenTree::Subtree(subtree)) => {
69 self.cached_cursor.set(cursor.subtree().unwrap());
70 cached.push(Some(convert_delim(subtree.delimiter, false)));
71 } 62 }
72 None => { 63
73 if let Some(subtree) = cursor.end() { 64 match cursor.token_tree() {
74 cached.push(Some(convert_delim(subtree.delimiter, true))); 65 Some(tt::TokenTree::Leaf(leaf)) => {
66 cached.push(Some(convert_leaf(&leaf)));
75 self.cached_cursor.set(cursor.bump()); 67 self.cached_cursor.set(cursor.bump());
76 } 68 }
69 Some(tt::TokenTree::Subtree(subtree)) => {
70 self.cached_cursor.set(cursor.subtree().unwrap());
71 cached.push(Some(convert_delim(subtree.delimiter, false)));
72 }
73 None => {
74 if let Some(subtree) = cursor.end() {
75 cached.push(Some(convert_delim(subtree.delimiter, true)));
76 self.cached_cursor.set(cursor.bump());
77 }
78 }
77 } 79 }
78 } 80 }
79 } 81 }
80 82
81 cached[pos].clone() 83 Ref::map(self.cached.borrow(), |c| &c[pos])
82 } 84 }
83} 85}
84 86
@@ -103,8 +105,8 @@ impl<'a> TokenSource for SubtreeTokenSource<'a> {
103 105
104 /// Is the current token a specified keyword? 106 /// Is the current token a specified keyword?
105 fn is_keyword(&self, kw: &str) -> bool { 107 fn is_keyword(&self, kw: &str) -> bool {
106 match self.get(self.curr.1) { 108 match *self.get(self.curr.1) {
107 Some(t) => t.text == *kw, 109 Some(ref t) => t.text == *kw,
108 _ => false, 110 _ => false,
109 } 111 }
110 } 112 }
diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs
index 658034097..beedac457 100644
--- a/crates/ra_parser/src/grammar.rs
+++ b/crates/ra_parser/src/grammar.rs
@@ -287,6 +287,16 @@ fn name_ref(p: &mut Parser) {
287 } 287 }
288} 288}
289 289
290fn name_ref_or_index(p: &mut Parser) {
291 if p.at(IDENT) || p.at(INT_NUMBER) {
292 let m = p.start();
293 p.bump();
294 m.complete(p, NAME_REF);
295 } else {
296 p.err_and_bump("expected identifier");
297 }
298}
299
290fn error_block(p: &mut Parser, message: &str) { 300fn error_block(p: &mut Parser, message: &str) {
291 assert!(p.at(T!['{'])); 301 assert!(p.at(T!['{']));
292 let m = p.start(); 302 let m = p.start();
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index b60a2f68c..742076c1a 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -489,10 +489,8 @@ fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
489 assert!(p.at(T![.])); 489 assert!(p.at(T![.]));
490 let m = lhs.precede(p); 490 let m = lhs.precede(p);
491 p.bump(); 491 p.bump();
492 if p.at(IDENT) { 492 if p.at(IDENT) || p.at(INT_NUMBER) {
493 name_ref(p) 493 name_ref_or_index(p)
494 } else if p.at(INT_NUMBER) {
495 p.bump();
496 } else if p.at(FLOAT_NUMBER) { 494 } else if p.at(FLOAT_NUMBER) {
497 // FIXME: How to recover and instead parse INT + T![.]? 495 // FIXME: How to recover and instead parse INT + T![.]?
498 p.bump(); 496 p.bump();
@@ -577,6 +575,7 @@ fn path_expr(p: &mut Parser, r: Restrictions) -> (CompletedMarker, BlockLike) {
577// S {}; 575// S {};
578// S { x, y: 32, }; 576// S { x, y: 32, };
579// S { x, y: 32, ..Default::default() }; 577// S { x, y: 32, ..Default::default() };
578// TupleStruct { 0: 1 };
580// } 579// }
581pub(crate) fn named_field_list(p: &mut Parser) { 580pub(crate) fn named_field_list(p: &mut Parser) {
582 assert!(p.at(T!['{'])); 581 assert!(p.at(T!['{']));
@@ -588,10 +587,10 @@ pub(crate) fn named_field_list(p: &mut Parser) {
588 // fn main() { 587 // fn main() {
589 // S { #[cfg(test)] field: 1 } 588 // S { #[cfg(test)] field: 1 }
590 // } 589 // }
591 IDENT | T![#] => { 590 IDENT | INT_NUMBER | T![#] => {
592 let m = p.start(); 591 let m = p.start();
593 attributes::outer_attributes(p); 592 attributes::outer_attributes(p);
594 name_ref(p); 593 name_ref_or_index(p);
595 if p.eat(T![:]) { 594 if p.eat(T![:]) {
596 expr(p); 595 expr(p);
597 } 596 }
diff --git a/crates/ra_parser/src/grammar/params.rs b/crates/ra_parser/src/grammar/params.rs
index 723b56343..0b09f1874 100644
--- a/crates/ra_parser/src/grammar/params.rs
+++ b/crates/ra_parser/src/grammar/params.rs
@@ -41,9 +41,20 @@ fn list_(p: &mut Parser, flavor: Flavor) {
41 let m = p.start(); 41 let m = p.start();
42 p.bump(); 42 p.bump();
43 if flavor.type_required() { 43 if flavor.type_required() {
44 // test self_param_outer_attr
45 // fn f(#[must_use] self) {}
46 attributes::outer_attributes(p);
44 opt_self_param(p); 47 opt_self_param(p);
45 } 48 }
46 while !p.at(EOF) && !p.at(ket) && !(flavor.type_required() && p.at(T![...])) { 49 while !p.at(EOF) && !p.at(ket) {
50 // test param_outer_arg
51 // fn f(#[attr1] pat: Type) {}
52 attributes::outer_attributes(p);
53
54 if flavor.type_required() && p.at(T![...]) {
55 break;
56 }
57
47 if !p.at_ts(VALUE_PARAMETER_FIRST) { 58 if !p.at_ts(VALUE_PARAMETER_FIRST) {
48 p.error("expected value parameter"); 59 p.error("expected value parameter");
49 break; 60 break;
diff --git a/crates/ra_parser/src/grammar/type_args.rs b/crates/ra_parser/src/grammar/type_args.rs
index f391b63db..3db08b280 100644
--- a/crates/ra_parser/src/grammar/type_args.rs
+++ b/crates/ra_parser/src/grammar/type_args.rs
@@ -35,6 +35,13 @@ fn type_arg(p: &mut Parser) {
35 p.bump(); 35 p.bump();
36 m.complete(p, LIFETIME_ARG); 36 m.complete(p, LIFETIME_ARG);
37 } 37 }
38 // test associated_type_bounds
39 // fn print_all<T: Iterator<Item: Display>>(printables: T) {}
40 IDENT if p.nth(1) == T![:] => {
41 name_ref(p);
42 type_params::bounds(p);
43 m.complete(p, ASSOC_TYPE_ARG);
44 }
38 IDENT if p.nth(1) == T![=] => { 45 IDENT if p.nth(1) == T![=] => {
39 name_ref(p); 46 name_ref(p);
40 p.bump(); 47 p.bump();
diff --git a/crates/ra_parser/src/parser.rs b/crates/ra_parser/src/parser.rs
index 159ed50df..393586561 100644
--- a/crates/ra_parser/src/parser.rs
+++ b/crates/ra_parser/src/parser.rs
@@ -6,7 +6,7 @@ use crate::{
6 event::Event, 6 event::Event,
7 ParseError, 7 ParseError,
8 SyntaxKind::{self, EOF, ERROR, TOMBSTONE}, 8 SyntaxKind::{self, EOF, ERROR, TOMBSTONE},
9 TokenSet, TokenSource, T, 9 Token, TokenSet, TokenSource, T,
10}; 10};
11 11
12/// `Parser` struct provides the low-level API for 12/// `Parser` struct provides the low-level API for
@@ -87,8 +87,9 @@ impl<'t> Parser<'t> {
87 let mut i = 0; 87 let mut i = 0;
88 88
89 loop { 89 loop {
90 let mut kind = self.token_source.lookahead_nth(i).kind; 90 let token = self.token_source.lookahead_nth(i);
91 if let Some((composited, step)) = self.is_composite(kind, i) { 91 let mut kind = token.kind;
92 if let Some((composited, step)) = self.is_composite(token, i) {
92 kind = composited; 93 kind = composited;
93 i += step; 94 i += step;
94 } else { 95 } else {
@@ -250,32 +251,47 @@ impl<'t> Parser<'t> {
250 } 251 }
251 252
252 /// helper function for check if it is composite. 253 /// helper function for check if it is composite.
253 fn is_composite(&self, kind: SyntaxKind, n: usize) -> Option<(SyntaxKind, usize)> { 254 fn is_composite(&self, first: Token, n: usize) -> Option<(SyntaxKind, usize)> {
254 // We assume the dollars will not occuried between 255 // We assume the dollars will not occuried between
255 // mult-byte tokens 256 // mult-byte tokens
256 257
257 let first = self.token_source.lookahead_nth(n); 258 let jn1 = first.is_jointed_to_next;
259 if !jn1 && first.kind != T![-] {
260 return None;
261 }
262
258 let second = self.token_source.lookahead_nth(n + 1); 263 let second = self.token_source.lookahead_nth(n + 1);
264 if first.kind == T![-] && second.kind == T![>] {
265 return Some((T![->], 2));
266 }
267 if !jn1 {
268 return None;
269 }
270
271 match (first.kind, second.kind) {
272 (T![:], T![:]) => return Some((T![::], 2)),
273 (T![=], T![=]) => return Some((T![==], 2)),
274 (T![=], T![>]) => return Some((T![=>], 2)),
275 (T![!], T![=]) => return Some((T![!=], 2)),
276 _ => {}
277 }
278
279 if first.kind != T![.] || second.kind != T![.] {
280 return None;
281 }
282
259 let third = self.token_source.lookahead_nth(n + 2); 283 let third = self.token_source.lookahead_nth(n + 2);
260 284
261 let jn1 = first.is_jointed_to_next;
262 let la2 = second.kind;
263 let jn2 = second.is_jointed_to_next; 285 let jn2 = second.is_jointed_to_next;
264 let la3 = third.kind; 286 let la3 = third.kind;
265 287
266 match kind { 288 if jn2 && la3 == T![.] {
267 T![.] if jn1 && la2 == T![.] && jn2 && la3 == T![.] => Some((T![...], 3)), 289 return Some((T![...], 3));
268 T![.] if jn1 && la2 == T![.] && la3 == T![=] => Some((T![..=], 3)), 290 }
269 T![.] if jn1 && la2 == T![.] => Some((T![..], 2)), 291 if la3 == T![=] {
270 292 return Some((T![..=], 3));
271 T![:] if jn1 && la2 == T![:] => Some((T![::], 2)),
272 T![=] if jn1 && la2 == T![=] => Some((T![==], 2)),
273 T![=] if jn1 && la2 == T![>] => Some((T![=>], 2)),
274
275 T![!] if jn1 && la2 == T![=] => Some((T![!=], 2)),
276 T![-] if la2 == T![>] => Some((T![->], 2)),
277 _ => None,
278 } 293 }
294 return Some((T![..], 2));
279 } 295 }
280 296
281 fn eat_dollars(&mut self) { 297 fn eat_dollars(&mut self) {
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml
index 3545d23c9..4fd6c75ef 100644
--- a/crates/ra_project_model/Cargo.toml
+++ b/crates/ra_project_model/Cargo.toml
@@ -7,7 +7,6 @@ authors = ["rust-analyzer developers"]
7[dependencies] 7[dependencies]
8log = "0.4.5" 8log = "0.4.5"
9rustc-hash = "1.0" 9rustc-hash = "1.0"
10relative-path = "0.4.0"
11 10
12cargo_metadata = "0.8.0" 11cargo_metadata = "0.8.0"
13 12
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 2b06e9e37..712d8818f 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -167,7 +167,7 @@ impl CargoWorkspace {
167 Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root }) 167 Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root })
168 } 168 }
169 169
170 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + 'a { 170 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
171 self.packages.iter().map(|(id, _pkg)| id) 171 self.packages.iter().map(|(id, _pkg)| id)
172 } 172 }
173 173
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 08e5c1c32..55b94b911 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -9,14 +9,10 @@ use std::{
9 path::{Path, PathBuf}, 9 path::{Path, PathBuf},
10}; 10};
11 11
12use rustc_hash::FxHashMap;
13
14use ra_db::{CrateGraph, Edition, FileId}; 12use ra_db::{CrateGraph, Edition, FileId};
15 13use rustc_hash::FxHashMap;
16use serde_json::from_reader; 14use serde_json::from_reader;
17 15
18use relative_path::RelativePath;
19
20pub use crate::{ 16pub use crate::{
21 cargo_workspace::{CargoWorkspace, Package, Target, TargetKind}, 17 cargo_workspace::{CargoWorkspace, Package, Target, TargetKind},
22 json_project::JsonProject, 18 json_project::JsonProject,
@@ -34,20 +30,20 @@ pub enum ProjectWorkspace {
34 Json { project: JsonProject }, 30 Json { project: JsonProject },
35} 31}
36 32
37/// `ProjectRoot` describes a workspace root folder. 33/// `PackageRoot` describes a package root folder.
38/// Which may be an external dependency, or a member of 34/// Which may be an external dependency, or a member of
39/// the current workspace. 35/// the current workspace.
40#[derive(Clone)] 36#[derive(Clone)]
41pub struct ProjectRoot { 37pub struct PackageRoot {
42 /// Path to the root folder 38 /// Path to the root folder
43 path: PathBuf, 39 path: PathBuf,
44 /// Is a member of the current workspace 40 /// Is a member of the current workspace
45 is_member: bool, 41 is_member: bool,
46} 42}
47 43
48impl ProjectRoot { 44impl PackageRoot {
49 pub fn new(path: PathBuf, is_member: bool) -> ProjectRoot { 45 pub fn new(path: PathBuf, is_member: bool) -> PackageRoot {
50 ProjectRoot { path, is_member } 46 PackageRoot { path, is_member }
51 } 47 }
52 48
53 pub fn path(&self) -> &PathBuf { 49 pub fn path(&self) -> &PathBuf {
@@ -57,28 +53,6 @@ impl ProjectRoot {
57 pub fn is_member(&self) -> bool { 53 pub fn is_member(&self) -> bool {
58 self.is_member 54 self.is_member
59 } 55 }
60
61 pub fn include_dir(&self, dir_path: &RelativePath) -> bool {
62 const COMMON_IGNORED_DIRS: &[&str] = &["node_modules", "target", ".git"];
63 const EXTERNAL_IGNORED_DIRS: &[&str] = &["examples", "tests", "benches"];
64
65 let is_ignored = if self.is_member {
66 dir_path.components().any(|c| COMMON_IGNORED_DIRS.contains(&c.as_str()))
67 } else {
68 dir_path.components().any(|c| {
69 let path = c.as_str();
70 COMMON_IGNORED_DIRS.contains(&path) || EXTERNAL_IGNORED_DIRS.contains(&path)
71 })
72 };
73
74 let hidden = dir_path.components().any(|c| c.as_str().starts_with('.'));
75
76 !is_ignored && !hidden
77 }
78
79 pub fn include_file(&self, file_path: &RelativePath) -> bool {
80 file_path.extension() == Some("rs")
81 }
82} 56}
83 57
84impl ProjectWorkspace { 58impl ProjectWorkspace {
@@ -99,38 +73,39 @@ impl ProjectWorkspace {
99 } 73 }
100 } 74 }
101 75
102 /// Returns the roots for the current ProjectWorkspace 76 /// Returns the roots for the current `ProjectWorkspace`
103 /// The return type contains the path and whether or not 77 /// The return type contains the path and whether or not
104 /// the root is a member of the current workspace 78 /// the root is a member of the current workspace
105 pub fn to_roots(&self) -> Vec<ProjectRoot> { 79 pub fn to_roots(&self) -> Vec<PackageRoot> {
106 match self { 80 match self {
107 ProjectWorkspace::Json { project } => { 81 ProjectWorkspace::Json { project } => {
108 let mut roots = Vec::with_capacity(project.roots.len()); 82 let mut roots = Vec::with_capacity(project.roots.len());
109 for root in &project.roots { 83 for root in &project.roots {
110 roots.push(ProjectRoot::new(root.path.clone(), true)); 84 roots.push(PackageRoot::new(root.path.clone(), true));
111 } 85 }
112 roots 86 roots
113 } 87 }
114 ProjectWorkspace::Cargo { cargo, sysroot } => { 88 ProjectWorkspace::Cargo { cargo, sysroot } => {
115 let mut roots = 89 let mut roots = Vec::with_capacity(cargo.packages().len() + sysroot.crates().len());
116 Vec::with_capacity(cargo.packages().count() + sysroot.crates().count());
117 for pkg in cargo.packages() { 90 for pkg in cargo.packages() {
118 let root = pkg.root(&cargo).to_path_buf(); 91 let root = pkg.root(&cargo).to_path_buf();
119 let member = pkg.is_member(&cargo); 92 let member = pkg.is_member(&cargo);
120 roots.push(ProjectRoot::new(root, member)); 93 roots.push(PackageRoot::new(root, member));
121 } 94 }
122 for krate in sysroot.crates() { 95 for krate in sysroot.crates() {
123 roots.push(ProjectRoot::new(krate.root_dir(&sysroot).to_path_buf(), false)) 96 roots.push(PackageRoot::new(krate.root_dir(&sysroot).to_path_buf(), false))
124 } 97 }
125 roots 98 roots
126 } 99 }
127 } 100 }
128 } 101 }
129 102
130 pub fn count(&self) -> usize { 103 pub fn n_packages(&self) -> usize {
131 match self { 104 match self {
132 ProjectWorkspace::Json { project } => project.crates.len(), 105 ProjectWorkspace::Json { project } => project.crates.len(),
133 ProjectWorkspace::Cargo { cargo, .. } => cargo.packages().count(), 106 ProjectWorkspace::Cargo { cargo, sysroot } => {
107 cargo.packages().len() + sysroot.crates().len()
108 }
134 } 109 }
135 } 110 }
136 111
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs
index 4f6e880dd..3f34d51cc 100644
--- a/crates/ra_project_model/src/sysroot.rs
+++ b/crates/ra_project_model/src/sysroot.rs
@@ -28,7 +28,7 @@ impl Sysroot {
28 self.by_name("std") 28 self.by_name("std")
29 } 29 }
30 30
31 pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + 'a { 31 pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a {
32 self.crates.iter().map(|(id, _data)| id) 32 self.crates.iter().map(|(id, _data)| id)
33 } 33 }
34 34
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index bc1c88070..5f8585878 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -8,7 +8,6 @@ description = "Comment and whitespace preserving parser for the Rust langauge"
8repository = "https://github.com/rust-analyzer/rust-analyzer" 8repository = "https://github.com/rust-analyzer/rust-analyzer"
9 9
10[dependencies] 10[dependencies]
11unicode-xid = "0.1.0"
12itertools = "0.8.0" 11itertools = "0.8.0"
13rowan = "0.6.1" 12rowan = "0.6.1"
14ra_rustc_lexer = { version = "0.1.0-pre.2" } 13ra_rustc_lexer = { version = "0.1.0-pre.2" }
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index c5746d98d..6f0489617 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -139,6 +139,52 @@ fn test_doc_comment_preserves_newlines() {
139} 139}
140 140
141#[test] 141#[test]
142fn test_doc_comment_single_line_block_strips_suffix() {
143 let file = SourceFile::parse(
144 r#"
145 /** this is mod foo*/
146 mod foo {}
147 "#,
148 )
149 .ok()
150 .unwrap();
151 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
152 assert_eq!("this is mod foo", module.doc_comment_text().unwrap());
153}
154
155#[test]
156fn test_doc_comment_single_line_block_strips_suffix_whitespace() {
157 let file = SourceFile::parse(
158 r#"
159 /** this is mod foo */
160 mod foo {}
161 "#,
162 )
163 .ok()
164 .unwrap();
165 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
166 assert_eq!("this is mod foo", module.doc_comment_text().unwrap());
167}
168
169#[test]
170fn test_doc_comment_multi_line_block_strips_suffix() {
171 let file = SourceFile::parse(
172 r#"
173 /**
174 this
175 is
176 mod foo
177 */
178 mod foo {}
179 "#,
180 )
181 .ok()
182 .unwrap();
183 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
184 assert_eq!(" this\n is\n mod foo", module.doc_comment_text().unwrap());
185}
186
187#[test]
142fn test_where_predicates() { 188fn test_where_predicates() {
143 fn assert_bound(text: &str, bound: Option<TypeBound>) { 189 fn assert_bound(text: &str, bound: Option<TypeBound>) {
144 assert_eq!(text, bound.unwrap().syntax().text().to_string()); 190 assert_eq!(text, bound.unwrap().syntax().text().to_string());
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs
index d4873b39a..2a59cf653 100644
--- a/crates/ra_syntax/src/ast/extensions.rs
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -91,6 +91,7 @@ impl ast::Attr {
91#[derive(Debug, Clone, PartialEq, Eq)] 91#[derive(Debug, Clone, PartialEq, Eq)]
92pub enum PathSegmentKind { 92pub enum PathSegmentKind {
93 Name(ast::NameRef), 93 Name(ast::NameRef),
94 Type { type_ref: Option<ast::TypeRef>, trait_ref: Option<ast::PathType> },
94 SelfKw, 95 SelfKw,
95 SuperKw, 96 SuperKw,
96 CrateKw, 97 CrateKw,
@@ -112,6 +113,15 @@ impl ast::PathSegment {
112 T![self] => PathSegmentKind::SelfKw, 113 T![self] => PathSegmentKind::SelfKw,
113 T![super] => PathSegmentKind::SuperKw, 114 T![super] => PathSegmentKind::SuperKw,
114 T![crate] => PathSegmentKind::CrateKw, 115 T![crate] => PathSegmentKind::CrateKw,
116 T![<] => {
117 // <T> or <T as Trait>
118 // T is any TypeRef, Trait has to be a PathType
119 let mut type_refs =
120 self.syntax().children().filter(|node| ast::TypeRef::can_cast(node.kind()));
121 let type_ref = type_refs.next().and_then(ast::TypeRef::cast);
122 let trait_ref = type_refs.next().and_then(ast::PathType::cast);
123 PathSegmentKind::Type { type_ref, trait_ref }
124 }
115 _ => return None, 125 _ => return None,
116 } 126 }
117 }; 127 };
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index da8cf4ae8..f322e1d84 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -2013,6 +2013,7 @@ impl AstNode for Param {
2013 2013
2014 2014
2015impl ast::TypeAscriptionOwner for Param {} 2015impl ast::TypeAscriptionOwner for Param {}
2016impl ast::AttrsOwner for Param {}
2016impl Param { 2017impl Param {
2017 pub fn pat(&self) -> Option<Pat> { 2018 pub fn pat(&self) -> Option<Pat> {
2018 super::child_opt(self) 2019 super::child_opt(self)
@@ -2667,6 +2668,7 @@ impl AstNode for SelfParam {
2667 2668
2668 2669
2669impl ast::TypeAscriptionOwner for SelfParam {} 2670impl ast::TypeAscriptionOwner for SelfParam {}
2671impl ast::AttrsOwner for SelfParam {}
2670impl SelfParam {} 2672impl SelfParam {}
2671 2673
2672// SlicePat 2674// SlicePat
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs
index 6ed1b5213..1b9a2b20c 100644
--- a/crates/ra_syntax/src/ast/traits.rs
+++ b/crates/ra_syntax/src/ast/traits.rs
@@ -115,8 +115,8 @@ pub trait DocCommentsOwner: AstNode {
115 } 115 }
116 116
117 /// Returns the textual content of a doc comment block as a single string. 117 /// Returns the textual content of a doc comment block as a single string.
118 /// That is, strips leading `///` (+ optional 1 character of whitespace) 118 /// That is, strips leading `///` (+ optional 1 character of whitespace),
119 /// and joins lines. 119 /// trailing `*/`, trailing whitespace and then joins the lines.
120 fn doc_comment_text(&self) -> Option<String> { 120 fn doc_comment_text(&self) -> Option<String> {
121 let mut has_comments = false; 121 let mut has_comments = false;
122 let docs = self 122 let docs = self
@@ -136,7 +136,13 @@ pub trait DocCommentsOwner: AstNode {
136 prefix_len 136 prefix_len
137 }; 137 };
138 138
139 line[pos..].to_owned() 139 let end = if comment.kind().shape.is_block() && line.ends_with("*/") {
140 line.len() - 2
141 } else {
142 line.len()
143 };
144
145 line[pos..end].trim_end().to_owned()
140 }) 146 })
141 .join("\n"); 147 .join("\n");
142 148
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index 817dedfbf..f2c20573e 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -642,12 +642,14 @@ Grammar(
642 "SelfParam": ( 642 "SelfParam": (
643 traits: [ 643 traits: [
644 "TypeAscriptionOwner", 644 "TypeAscriptionOwner",
645 "AttrsOwner",
645 ] 646 ]
646 ), 647 ),
647 "Param": ( 648 "Param": (
648 options: [ "Pat" ], 649 options: [ "Pat" ],
649 traits: [ 650 traits: [
650 "TypeAscriptionOwner", 651 "TypeAscriptionOwner",
652 "AttrsOwner",
651 ] 653 ]
652 ), 654 ),
653 "UseItem": ( 655 "UseItem": (
diff --git a/crates/ra_syntax/src/parsing/lexer.rs b/crates/ra_syntax/src/parsing/lexer.rs
index 2a4343b0a..06822ea91 100644
--- a/crates/ra_syntax/src/parsing/lexer.rs
+++ b/crates/ra_syntax/src/parsing/lexer.rs
@@ -12,6 +12,19 @@ pub struct Token {
12 pub len: TextUnit, 12 pub len: TextUnit,
13} 13}
14 14
15fn match_literal_kind(kind: ra_rustc_lexer::LiteralKind) -> SyntaxKind {
16 match kind {
17 ra_rustc_lexer::LiteralKind::Int { .. } => INT_NUMBER,
18 ra_rustc_lexer::LiteralKind::Float { .. } => FLOAT_NUMBER,
19 ra_rustc_lexer::LiteralKind::Char { .. } => CHAR,
20 ra_rustc_lexer::LiteralKind::Byte { .. } => BYTE,
21 ra_rustc_lexer::LiteralKind::Str { .. } => STRING,
22 ra_rustc_lexer::LiteralKind::ByteStr { .. } => BYTE_STRING,
23 ra_rustc_lexer::LiteralKind::RawStr { .. } => RAW_STRING,
24 ra_rustc_lexer::LiteralKind::RawByteStr { .. } => RAW_BYTE_STRING,
25 }
26}
27
15/// Break a string up into its component tokens 28/// Break a string up into its component tokens
16pub fn tokenize(text: &str) -> Vec<Token> { 29pub fn tokenize(text: &str) -> Vec<Token> {
17 if text.is_empty() { 30 if text.is_empty() {
@@ -53,16 +66,7 @@ pub fn tokenize(text: &str) -> Vec<Token> {
53 } 66 }
54 } 67 }
55 ra_rustc_lexer::TokenKind::RawIdent => IDENT, 68 ra_rustc_lexer::TokenKind::RawIdent => IDENT,
56 ra_rustc_lexer::TokenKind::Literal { kind, .. } => match kind { 69 ra_rustc_lexer::TokenKind::Literal { kind, .. } => match_literal_kind(kind),
57 ra_rustc_lexer::LiteralKind::Int { .. } => INT_NUMBER,
58 ra_rustc_lexer::LiteralKind::Float { .. } => FLOAT_NUMBER,
59 ra_rustc_lexer::LiteralKind::Char { .. } => CHAR,
60 ra_rustc_lexer::LiteralKind::Byte { .. } => BYTE,
61 ra_rustc_lexer::LiteralKind::Str { .. } => STRING,
62 ra_rustc_lexer::LiteralKind::ByteStr { .. } => BYTE_STRING,
63 ra_rustc_lexer::LiteralKind::RawStr { .. } => RAW_STRING,
64 ra_rustc_lexer::LiteralKind::RawByteStr { .. } => RAW_BYTE_STRING,
65 },
66 ra_rustc_lexer::TokenKind::Lifetime { .. } => LIFETIME, 70 ra_rustc_lexer::TokenKind::Lifetime { .. } => LIFETIME,
67 ra_rustc_lexer::TokenKind::Semi => SEMI, 71 ra_rustc_lexer::TokenKind::Semi => SEMI,
68 ra_rustc_lexer::TokenKind::Comma => COMMA, 72 ra_rustc_lexer::TokenKind::Comma => COMMA,
@@ -131,16 +135,7 @@ pub fn classify_literal(text: &str) -> Option<Token> {
131 return None; 135 return None;
132 } 136 }
133 let kind = match t.kind { 137 let kind = match t.kind {
134 ra_rustc_lexer::TokenKind::Literal { kind, .. } => match kind { 138 ra_rustc_lexer::TokenKind::Literal { kind, .. } => match_literal_kind(kind),
135 ra_rustc_lexer::LiteralKind::Int { .. } => INT_NUMBER,
136 ra_rustc_lexer::LiteralKind::Float { .. } => FLOAT_NUMBER,
137 ra_rustc_lexer::LiteralKind::Char { .. } => CHAR,
138 ra_rustc_lexer::LiteralKind::Byte { .. } => BYTE,
139 ra_rustc_lexer::LiteralKind::Str { .. } => STRING,
140 ra_rustc_lexer::LiteralKind::ByteStr { .. } => BYTE_STRING,
141 ra_rustc_lexer::LiteralKind::RawStr { .. } => RAW_STRING,
142 ra_rustc_lexer::LiteralKind::RawByteStr { .. } => RAW_BYTE_STRING,
143 },
144 _ => return None, 139 _ => return None,
145 }; 140 };
146 Some(Token { kind, len: TextUnit::from_usize(t.len) }) 141 Some(Token { kind, len: TextUnit::from_usize(t.len) })
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index 1f904434e..2bb3c0a03 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -1,13 +1,12 @@
1mod block; 1mod block;
2mod field_expr;
3 2
4use ra_rustc_lexer::unescape; 3use ra_rustc_lexer::unescape;
5 4
6use crate::{ 5use crate::{
7 algo::visit::{visitor_ctx, VisitorCtx}, 6 algo::visit::{visitor_ctx, VisitorCtx},
8 ast, SyntaxError, SyntaxErrorKind, 7 ast, AstNode, SyntaxError, SyntaxErrorKind,
9 SyntaxKind::{BYTE, BYTE_STRING, CHAR, STRING}, 8 SyntaxKind::{BYTE, BYTE_STRING, CHAR, INT_NUMBER, STRING},
10 SyntaxNode, TextUnit, T, 9 SyntaxNode, SyntaxToken, TextUnit, T,
11}; 10};
12 11
13#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 12#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -101,7 +100,8 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
101 let _ = visitor_ctx(&mut errors) 100 let _ = visitor_ctx(&mut errors)
102 .visit::<ast::Literal, _>(validate_literal) 101 .visit::<ast::Literal, _>(validate_literal)
103 .visit::<ast::Block, _>(block::validate_block_node) 102 .visit::<ast::Block, _>(block::validate_block_node)
104 .visit::<ast::FieldExpr, _>(field_expr::validate_field_expr_node) 103 .visit::<ast::FieldExpr, _>(|it, errors| validate_numeric_name(it.name_ref(), errors))
104 .visit::<ast::NamedField, _>(|it, errors| validate_numeric_name(it.name_ref(), errors))
105 .accept(&node); 105 .accept(&node);
106 } 106 }
107 errors 107 errors
@@ -189,3 +189,18 @@ pub(crate) fn validate_block_structure(root: &SyntaxNode) {
189 } 189 }
190 } 190 }
191} 191}
192
193fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<SyntaxError>) {
194 if let Some(int_token) = int_token(name_ref) {
195 if int_token.text().chars().any(|c| !c.is_digit(10)) {
196 errors.push(SyntaxError::new(
197 SyntaxErrorKind::InvalidTupleIndexFormat,
198 int_token.text_range(),
199 ));
200 }
201 }
202
203 fn int_token(name_ref: Option<ast::NameRef>) -> Option<SyntaxToken> {
204 name_ref?.syntax().first_child_or_token()?.into_token().filter(|it| it.kind() == INT_NUMBER)
205 }
206}
diff --git a/crates/ra_syntax/src/validation/field_expr.rs b/crates/ra_syntax/src/validation/field_expr.rs
deleted file mode 100644
index 004f199fd..000000000
--- a/crates/ra_syntax/src/validation/field_expr.rs
+++ /dev/null
@@ -1,13 +0,0 @@
1use crate::{
2 ast::{self, FieldKind},
3 SyntaxError,
4 SyntaxErrorKind::*,
5};
6
7pub(crate) fn validate_field_expr_node(node: ast::FieldExpr, errors: &mut Vec<SyntaxError>) {
8 if let Some(FieldKind::Index(idx)) = node.field_access() {
9 if idx.text().chars().any(|c| c < '0' || c > '9') {
10 errors.push(SyntaxError::new(InvalidTupleIndexFormat, idx.text_range()));
11 }
12 }
13}
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0010_bad_tuple_index_expr.txt b/crates/ra_syntax/test_data/parser/inline/err/0010_bad_tuple_index_expr.txt
index a21b29c80..465e79e7b 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0010_bad_tuple_index_expr.txt
+++ b/crates/ra_syntax/test_data/parser/inline/err/0010_bad_tuple_index_expr.txt
@@ -30,7 +30,8 @@ SOURCE_FILE@[0; 47)
30 NAME_REF@[25; 26) 30 NAME_REF@[25; 26)
31 IDENT@[25; 26) "x" 31 IDENT@[25; 26) "x"
32 DOT@[26; 27) "." 32 DOT@[26; 27) "."
33 INT_NUMBER@[27; 31) "1i32" 33 NAME_REF@[27; 31)
34 INT_NUMBER@[27; 31) "1i32"
34 SEMI@[31; 32) ";" 35 SEMI@[31; 32) ";"
35 WHITESPACE@[32; 37) "\n " 36 WHITESPACE@[32; 37) "\n "
36 EXPR_STMT@[37; 44) 37 EXPR_STMT@[37; 44)
@@ -41,11 +42,11 @@ SOURCE_FILE@[0; 47)
41 NAME_REF@[37; 38) 42 NAME_REF@[37; 38)
42 IDENT@[37; 38) "x" 43 IDENT@[37; 38) "x"
43 DOT@[38; 39) "." 44 DOT@[38; 39) "."
44 INT_NUMBER@[39; 43) "0x01" 45 NAME_REF@[39; 43)
46 INT_NUMBER@[39; 43) "0x01"
45 SEMI@[43; 44) ";" 47 SEMI@[43; 44) ";"
46 WHITESPACE@[44; 45) "\n" 48 WHITESPACE@[44; 45) "\n"
47 R_CURLY@[45; 46) "}" 49 R_CURLY@[45; 46) "}"
48 WHITESPACE@[46; 47) "\n" 50 WHITESPACE@[46; 47) "\n"
49error [17; 19): Tuple (struct) field access is only allowed through decimal integers with no underscores or suffix
50error [27; 31): Tuple (struct) field access is only allowed through decimal integers with no underscores or suffix 51error [27; 31): Tuple (struct) field access is only allowed through decimal integers with no underscores or suffix
51error [39; 43): Tuple (struct) field access is only allowed through decimal integers with no underscores or suffix 52error [39; 43): Tuple (struct) field access is only allowed through decimal integers with no underscores or suffix
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0011_field_expr.txt b/crates/ra_syntax/test_data/parser/inline/ok/0011_field_expr.txt
index 78054ec5a..1d2cf2761 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0011_field_expr.txt
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0011_field_expr.txt
@@ -32,7 +32,8 @@ SOURCE_FILE@[0; 48)
32 NAME_REF@[26; 27) 32 NAME_REF@[26; 27)
33 IDENT@[26; 27) "x" 33 IDENT@[26; 27) "x"
34 DOT@[27; 28) "." 34 DOT@[27; 28) "."
35 INT_NUMBER@[28; 29) "0" 35 NAME_REF@[28; 29)
36 INT_NUMBER@[28; 29) "0"
36 DOT@[29; 30) "." 37 DOT@[29; 30) "."
37 NAME_REF@[30; 33) 38 NAME_REF@[30; 33)
38 IDENT@[30; 33) "bar" 39 IDENT@[30; 33) "bar"
@@ -47,7 +48,8 @@ SOURCE_FILE@[0; 48)
47 NAME_REF@[39; 40) 48 NAME_REF@[39; 40)
48 IDENT@[39; 40) "x" 49 IDENT@[39; 40) "x"
49 DOT@[40; 41) "." 50 DOT@[40; 41) "."
50 INT_NUMBER@[41; 42) "0" 51 NAME_REF@[41; 42)
52 INT_NUMBER@[41; 42) "0"
51 ARG_LIST@[42; 44) 53 ARG_LIST@[42; 44)
52 L_PAREN@[42; 43) "(" 54 L_PAREN@[42; 43) "("
53 R_PAREN@[43; 44) ")" 55 R_PAREN@[43; 44) ")"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.rs b/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.rs
index eb711f68a..6285e5549 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.rs
@@ -2,4 +2,5 @@ fn foo() {
2 S {}; 2 S {};
3 S { x, y: 32, }; 3 S { x, y: 32, };
4 S { x, y: 32, ..Default::default() }; 4 S { x, y: 32, ..Default::default() };
5 TupleStruct { 0: 1 };
5} 6}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.txt b/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.txt
index 94d1bfe2e..d06594cae 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.txt
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.txt
@@ -1,5 +1,5 @@
1SOURCE_FILE@[0; 86) 1SOURCE_FILE@[0; 112)
2 FN_DEF@[0; 85) 2 FN_DEF@[0; 111)
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,7 +8,7 @@ SOURCE_FILE@[0; 86)
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@[9; 85) 11 BLOCK@[9; 111)
12 L_CURLY@[9; 10) "{" 12 L_CURLY@[9; 10) "{"
13 WHITESPACE@[10; 15) "\n " 13 WHITESPACE@[10; 15) "\n "
14 EXPR_STMT@[15; 20) 14 EXPR_STMT@[15; 20)
@@ -92,6 +92,27 @@ SOURCE_FILE@[0; 86)
92 WHITESPACE@[80; 81) " " 92 WHITESPACE@[80; 81) " "
93 R_CURLY@[81; 82) "}" 93 R_CURLY@[81; 82) "}"
94 SEMI@[82; 83) ";" 94 SEMI@[82; 83) ";"
95 WHITESPACE@[83; 84) "\n" 95 WHITESPACE@[83; 88) "\n "
96 R_CURLY@[84; 85) "}" 96 EXPR_STMT@[88; 109)
97 WHITESPACE@[85; 86) "\n" 97 STRUCT_LIT@[88; 108)
98 PATH@[88; 99)
99 PATH_SEGMENT@[88; 99)
100 NAME_REF@[88; 99)
101 IDENT@[88; 99) "TupleStruct"
102 WHITESPACE@[99; 100) " "
103 NAMED_FIELD_LIST@[100; 108)
104 L_CURLY@[100; 101) "{"
105 WHITESPACE@[101; 102) " "
106 NAMED_FIELD@[102; 106)
107 NAME_REF@[102; 103)
108 INT_NUMBER@[102; 103) "0"
109 COLON@[103; 104) ":"
110 WHITESPACE@[104; 105) " "
111 LITERAL@[105; 106)
112 INT_NUMBER@[105; 106) "1"
113 WHITESPACE@[106; 107) " "
114 R_CURLY@[107; 108) "}"
115 SEMI@[108; 109) ";"
116 WHITESPACE@[109; 110) "\n"
117 R_CURLY@[110; 111) "}"
118 WHITESPACE@[111; 112) "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0137_await_expr.txt b/crates/ra_syntax/test_data/parser/inline/ok/0137_await_expr.txt
index 99bd76ace..7adb662de 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0137_await_expr.txt
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0137_await_expr.txt
@@ -31,7 +31,8 @@ SOURCE_FILE@[0; 67)
31 NAME_REF@[28; 29) 31 NAME_REF@[28; 29)
32 IDENT@[28; 29) "x" 32 IDENT@[28; 29) "x"
33 DOT@[29; 30) "." 33 DOT@[29; 30) "."
34 INT_NUMBER@[30; 31) "0" 34 NAME_REF@[30; 31)
35 INT_NUMBER@[30; 31) "0"
35 DOT@[31; 32) "." 36 DOT@[31; 32) "."
36 AWAIT_KW@[32; 37) "await" 37 AWAIT_KW@[32; 37) "await"
37 SEMI@[37; 38) ";" 38 SEMI@[37; 38) ";"
@@ -48,7 +49,8 @@ SOURCE_FILE@[0; 67)
48 NAME_REF@[43; 44) 49 NAME_REF@[43; 44)
49 IDENT@[43; 44) "x" 50 IDENT@[43; 44) "x"
50 DOT@[44; 45) "." 51 DOT@[44; 45) "."
51 INT_NUMBER@[45; 46) "0" 52 NAME_REF@[45; 46)
53 INT_NUMBER@[45; 46) "0"
52 ARG_LIST@[46; 48) 54 ARG_LIST@[46; 48)
53 L_PAREN@[46; 47) "(" 55 L_PAREN@[46; 47) "("
54 R_PAREN@[47; 48) ")" 56 R_PAREN@[47; 48) ")"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.rs b/crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.rs
new file mode 100644
index 000000000..eb21a657b
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.rs
@@ -0,0 +1 @@
fn print_all<T: Iterator<Item: Display>>(printables: T) {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.txt b/crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.txt
new file mode 100644
index 000000000..33e75510d
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.txt
@@ -0,0 +1,55 @@
1SOURCE_FILE@[0; 59)
2 FN_DEF@[0; 58)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 12)
6 IDENT@[3; 12) "print_all"
7 TYPE_PARAM_LIST@[12; 40)
8 L_ANGLE@[12; 13) "<"
9 TYPE_PARAM@[13; 39)
10 NAME@[13; 14)
11 IDENT@[13; 14) "T"
12 COLON@[14; 15) ":"
13 WHITESPACE@[15; 16) " "
14 TYPE_BOUND_LIST@[16; 39)
15 TYPE_BOUND@[16; 39)
16 PATH_TYPE@[16; 39)
17 PATH@[16; 39)
18 PATH_SEGMENT@[16; 39)
19 NAME_REF@[16; 24)
20 IDENT@[16; 24) "Iterator"
21 TYPE_ARG_LIST@[24; 39)
22 L_ANGLE@[24; 25) "<"
23 ASSOC_TYPE_ARG@[25; 38)
24 NAME_REF@[25; 29)
25 IDENT@[25; 29) "Item"
26 COLON@[29; 30) ":"
27 WHITESPACE@[30; 31) " "
28 TYPE_BOUND_LIST@[31; 38)
29 TYPE_BOUND@[31; 38)
30 PATH_TYPE@[31; 38)
31 PATH@[31; 38)
32 PATH_SEGMENT@[31; 38)
33 NAME_REF@[31; 38)
34 IDENT@[31; 38) "Display"
35 R_ANGLE@[38; 39) ">"
36 R_ANGLE@[39; 40) ">"
37 PARAM_LIST@[40; 55)
38 L_PAREN@[40; 41) "("
39 PARAM@[41; 54)
40 BIND_PAT@[41; 51)
41 NAME@[41; 51)
42 IDENT@[41; 51) "printables"
43 COLON@[51; 52) ":"
44 WHITESPACE@[52; 53) " "
45 PATH_TYPE@[53; 54)
46 PATH@[53; 54)
47 PATH_SEGMENT@[53; 54)
48 NAME_REF@[53; 54)
49 IDENT@[53; 54) "T"
50 R_PAREN@[54; 55) ")"
51 WHITESPACE@[55; 56) " "
52 BLOCK@[56; 58)
53 L_CURLY@[56; 57) "{"
54 R_CURLY@[57; 58) "}"
55 WHITESPACE@[58; 59) "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rs b/crates/ra_syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rs
new file mode 100644
index 000000000..35155057a
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rs
@@ -0,0 +1 @@
fn f(#[must_use] self) {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.txt b/crates/ra_syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.txt
new file mode 100644
index 000000000..49b14e632
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.txt
@@ -0,0 +1,23 @@
1SOURCE_FILE@[0; 26)
2 FN_DEF@[0; 25)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 4)
6 IDENT@[3; 4) "f"
7 PARAM_LIST@[4; 22)
8 L_PAREN@[4; 5) "("
9 ATTR@[5; 16)
10 POUND@[5; 6) "#"
11 TOKEN_TREE@[6; 16)
12 L_BRACK@[6; 7) "["
13 IDENT@[7; 15) "must_use"
14 R_BRACK@[15; 16) "]"
15 WHITESPACE@[16; 17) " "
16 SELF_PARAM@[17; 21)
17 SELF_KW@[17; 21) "self"
18 R_PAREN@[21; 22) ")"
19 WHITESPACE@[22; 23) " "
20 BLOCK@[23; 25)
21 L_CURLY@[23; 24) "{"
22 R_CURLY@[24; 25) "}"
23 WHITESPACE@[25; 26) "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0139_param_outer_arg.rs b/crates/ra_syntax/test_data/parser/inline/ok/0139_param_outer_arg.rs
new file mode 100644
index 000000000..c238be791
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0139_param_outer_arg.rs
@@ -0,0 +1 @@
fn f(#[attr1] pat: Type) {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0139_param_outer_arg.txt b/crates/ra_syntax/test_data/parser/inline/ok/0139_param_outer_arg.txt
new file mode 100644
index 000000000..91c5e5f9a
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0139_param_outer_arg.txt
@@ -0,0 +1,32 @@
1SOURCE_FILE@[0; 28)
2 FN_DEF@[0; 27)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 4)
6 IDENT@[3; 4) "f"
7 PARAM_LIST@[4; 24)
8 L_PAREN@[4; 5) "("
9 ATTR@[5; 13)
10 POUND@[5; 6) "#"
11 TOKEN_TREE@[6; 13)
12 L_BRACK@[6; 7) "["
13 IDENT@[7; 12) "attr1"
14 R_BRACK@[12; 13) "]"
15 WHITESPACE@[13; 14) " "
16 PARAM@[14; 23)
17 BIND_PAT@[14; 17)
18 NAME@[14; 17)
19 IDENT@[14; 17) "pat"
20 COLON@[17; 18) ":"
21 WHITESPACE@[18; 19) " "
22 PATH_TYPE@[19; 23)
23 PATH@[19; 23)
24 PATH_SEGMENT@[19; 23)
25 NAME_REF@[19; 23)
26 IDENT@[19; 23) "Type"
27 R_PAREN@[23; 24) ")"
28 WHITESPACE@[24; 25) " "
29 BLOCK@[25; 27)
30 L_CURLY@[25; 26) "{"
31 R_CURLY@[26; 27) "}"
32 WHITESPACE@[27; 28) "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.rs b/crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.rs
new file mode 100644
index 000000000..de350d858
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.rs
@@ -0,0 +1,21 @@
1fn g1(#[attr1] #[attr2] pat: Type) {}
2fn g2(#[attr1] x: u8) {}
3
4extern "C" { fn printf(format: *const i8, #[attr] ...) -> i32; }
5
6fn foo<F: FnMut(#[attr] &mut Foo<'a>)>(){}
7
8trait Foo {
9 fn bar(#[attr] _: u64, # [attr] mut x: i32);
10}
11
12impl S {
13 fn f(#[must_use] self) {}
14 fn g1(#[attr] self) {}
15 fn g2(#[attr] &self) {}
16 fn g3<'a>(#[attr] &mut self) {}
17 fn g4<'a>(#[attr] &'a self) {}
18 fn g5<'a>(#[attr] &'a mut self) {}
19 fn c(#[attr] self: Self) {}
20 fn d(#[attr] self: Rc<Self>) {}
21} \ No newline at end of file
diff --git a/crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.txt b/crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.txt
new file mode 100644
index 000000000..b360f29f4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.txt
@@ -0,0 +1,477 @@
1SOURCE_FILE@[0; 519)
2 FN_DEF@[0; 37)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 5)
6 IDENT@[3; 5) "g1"
7 PARAM_LIST@[5; 34)
8 L_PAREN@[5; 6) "("
9 ATTR@[6; 14)
10 POUND@[6; 7) "#"
11 TOKEN_TREE@[7; 14)
12 L_BRACK@[7; 8) "["
13 IDENT@[8; 13) "attr1"
14 R_BRACK@[13; 14) "]"
15 WHITESPACE@[14; 15) " "
16 ATTR@[15; 23)
17 POUND@[15; 16) "#"
18 TOKEN_TREE@[16; 23)
19 L_BRACK@[16; 17) "["
20 IDENT@[17; 22) "attr2"
21 R_BRACK@[22; 23) "]"
22 WHITESPACE@[23; 24) " "
23 PARAM@[24; 33)
24 BIND_PAT@[24; 27)
25 NAME@[24; 27)
26 IDENT@[24; 27) "pat"
27 COLON@[27; 28) ":"
28 WHITESPACE@[28; 29) " "
29 PATH_TYPE@[29; 33)
30 PATH@[29; 33)
31 PATH_SEGMENT@[29; 33)
32 NAME_REF@[29; 33)
33 IDENT@[29; 33) "Type"
34 R_PAREN@[33; 34) ")"
35 WHITESPACE@[34; 35) " "
36 BLOCK@[35; 37)
37 L_CURLY@[35; 36) "{"
38 R_CURLY@[36; 37) "}"
39 WHITESPACE@[37; 38) "\n"
40 FN_DEF@[38; 62)
41 FN_KW@[38; 40) "fn"
42 WHITESPACE@[40; 41) " "
43 NAME@[41; 43)
44 IDENT@[41; 43) "g2"
45 PARAM_LIST@[43; 59)
46 L_PAREN@[43; 44) "("
47 ATTR@[44; 52)
48 POUND@[44; 45) "#"
49 TOKEN_TREE@[45; 52)
50 L_BRACK@[45; 46) "["
51 IDENT@[46; 51) "attr1"
52 R_BRACK@[51; 52) "]"
53 WHITESPACE@[52; 53) " "
54 PARAM@[53; 58)
55 BIND_PAT@[53; 54)
56 NAME@[53; 54)
57 IDENT@[53; 54) "x"
58 COLON@[54; 55) ":"
59 WHITESPACE@[55; 56) " "
60 PATH_TYPE@[56; 58)
61 PATH@[56; 58)
62 PATH_SEGMENT@[56; 58)
63 NAME_REF@[56; 58)
64 IDENT@[56; 58) "u8"
65 R_PAREN@[58; 59) ")"
66 WHITESPACE@[59; 60) " "
67 BLOCK@[60; 62)
68 L_CURLY@[60; 61) "{"
69 R_CURLY@[61; 62) "}"
70 WHITESPACE@[62; 64) "\n\n"
71 EXTERN_BLOCK@[64; 128)
72 ABI@[64; 74)
73 EXTERN_KW@[64; 70) "extern"
74 WHITESPACE@[70; 71) " "
75 STRING@[71; 74) "\"C\""
76 WHITESPACE@[74; 75) " "
77 EXTERN_ITEM_LIST@[75; 128)
78 L_CURLY@[75; 76) "{"
79 WHITESPACE@[76; 77) " "
80 FN_DEF@[77; 126)
81 FN_KW@[77; 79) "fn"
82 WHITESPACE@[79; 80) " "
83 NAME@[80; 86)
84 IDENT@[80; 86) "printf"
85 PARAM_LIST@[86; 118)
86 L_PAREN@[86; 87) "("
87 PARAM@[87; 104)
88 BIND_PAT@[87; 93)
89 NAME@[87; 93)
90 IDENT@[87; 93) "format"
91 COLON@[93; 94) ":"
92 WHITESPACE@[94; 95) " "
93 POINTER_TYPE@[95; 104)
94 STAR@[95; 96) "*"
95 CONST_KW@[96; 101) "const"
96 WHITESPACE@[101; 102) " "
97 PATH_TYPE@[102; 104)
98 PATH@[102; 104)
99 PATH_SEGMENT@[102; 104)
100 NAME_REF@[102; 104)
101 IDENT@[102; 104) "i8"
102 COMMA@[104; 105) ","
103 WHITESPACE@[105; 106) " "
104 ATTR@[106; 113)
105 POUND@[106; 107) "#"
106 TOKEN_TREE@[107; 113)
107 L_BRACK@[107; 108) "["
108 IDENT@[108; 112) "attr"
109 R_BRACK@[112; 113) "]"
110 WHITESPACE@[113; 114) " "
111 DOTDOTDOT@[114; 117) "..."
112 R_PAREN@[117; 118) ")"
113 WHITESPACE@[118; 119) " "
114 RET_TYPE@[119; 125)
115 THIN_ARROW@[119; 121) "->"
116 WHITESPACE@[121; 122) " "
117 PATH_TYPE@[122; 125)
118 PATH@[122; 125)
119 PATH_SEGMENT@[122; 125)
120 NAME_REF@[122; 125)
121 IDENT@[122; 125) "i32"
122 SEMI@[125; 126) ";"
123 WHITESPACE@[126; 127) " "
124 R_CURLY@[127; 128) "}"
125 WHITESPACE@[128; 130) "\n\n"
126 FN_DEF@[130; 172)
127 FN_KW@[130; 132) "fn"
128 WHITESPACE@[132; 133) " "
129 NAME@[133; 136)
130 IDENT@[133; 136) "foo"
131 TYPE_PARAM_LIST@[136; 168)
132 L_ANGLE@[136; 137) "<"
133 TYPE_PARAM@[137; 167)
134 NAME@[137; 138)
135 IDENT@[137; 138) "F"
136 COLON@[138; 139) ":"
137 WHITESPACE@[139; 140) " "
138 TYPE_BOUND_LIST@[140; 167)
139 TYPE_BOUND@[140; 167)
140 PATH_TYPE@[140; 167)
141 PATH@[140; 167)
142 PATH_SEGMENT@[140; 167)
143 NAME_REF@[140; 145)
144 IDENT@[140; 145) "FnMut"
145 PARAM_LIST@[145; 167)
146 L_PAREN@[145; 146) "("
147 ATTR@[146; 153)
148 POUND@[146; 147) "#"
149 TOKEN_TREE@[147; 153)
150 L_BRACK@[147; 148) "["
151 IDENT@[148; 152) "attr"
152 R_BRACK@[152; 153) "]"
153 WHITESPACE@[153; 154) " "
154 PARAM@[154; 166)
155 REFERENCE_TYPE@[154; 166)
156 AMP@[154; 155) "&"
157 MUT_KW@[155; 158) "mut"
158 WHITESPACE@[158; 159) " "
159 PATH_TYPE@[159; 166)
160 PATH@[159; 166)
161 PATH_SEGMENT@[159; 166)
162 NAME_REF@[159; 162)
163 IDENT@[159; 162) "Foo"
164 TYPE_ARG_LIST@[162; 166)
165 L_ANGLE@[162; 163) "<"
166 LIFETIME_ARG@[163; 165)
167 LIFETIME@[163; 165) "\'a"
168 R_ANGLE@[165; 166) ">"
169 R_PAREN@[166; 167) ")"
170 R_ANGLE@[167; 168) ">"
171 PARAM_LIST@[168; 170)
172 L_PAREN@[168; 169) "("
173 R_PAREN@[169; 170) ")"
174 BLOCK@[170; 172)
175 L_CURLY@[170; 171) "{"
176 R_CURLY@[171; 172) "}"
177 WHITESPACE@[172; 174) "\n\n"
178 TRAIT_DEF@[174; 236)
179 TRAIT_KW@[174; 179) "trait"
180 WHITESPACE@[179; 180) " "
181 NAME@[180; 183)
182 IDENT@[180; 183) "Foo"
183 WHITESPACE@[183; 184) " "
184 ITEM_LIST@[184; 236)
185 L_CURLY@[184; 185) "{"
186 WHITESPACE@[185; 190) "\n "
187 FN_DEF@[190; 234)
188 FN_KW@[190; 192) "fn"
189 WHITESPACE@[192; 193) " "
190 NAME@[193; 196)
191 IDENT@[193; 196) "bar"
192 PARAM_LIST@[196; 233)
193 L_PAREN@[196; 197) "("
194 ATTR@[197; 204)
195 POUND@[197; 198) "#"
196 TOKEN_TREE@[198; 204)
197 L_BRACK@[198; 199) "["
198 IDENT@[199; 203) "attr"
199 R_BRACK@[203; 204) "]"
200 WHITESPACE@[204; 205) " "
201 PARAM@[205; 211)
202 PLACEHOLDER_PAT@[205; 206)
203 UNDERSCORE@[205; 206) "_"
204 COLON@[206; 207) ":"
205 WHITESPACE@[207; 208) " "
206 PATH_TYPE@[208; 211)
207 PATH@[208; 211)
208 PATH_SEGMENT@[208; 211)
209 NAME_REF@[208; 211)
210 IDENT@[208; 211) "u64"
211 COMMA@[211; 212) ","
212 WHITESPACE@[212; 213) " "
213 ATTR@[213; 221)
214 POUND@[213; 214) "#"
215 WHITESPACE@[214; 215) " "
216 TOKEN_TREE@[215; 221)
217 L_BRACK@[215; 216) "["
218 IDENT@[216; 220) "attr"
219 R_BRACK@[220; 221) "]"
220 WHITESPACE@[221; 222) " "
221 PARAM@[222; 232)
222 BIND_PAT@[222; 227)
223 MUT_KW@[222; 225) "mut"
224 WHITESPACE@[225; 226) " "
225 NAME@[226; 227)
226 IDENT@[226; 227) "x"
227 COLON@[227; 228) ":"
228 WHITESPACE@[228; 229) " "
229 PATH_TYPE@[229; 232)
230 PATH@[229; 232)
231 PATH_SEGMENT@[229; 232)
232 NAME_REF@[229; 232)
233 IDENT@[229; 232) "i32"
234 R_PAREN@[232; 233) ")"
235 SEMI@[233; 234) ";"
236 WHITESPACE@[234; 235) "\n"
237 R_CURLY@[235; 236) "}"
238 WHITESPACE@[236; 238) "\n\n"
239 IMPL_BLOCK@[238; 519)
240 IMPL_KW@[238; 242) "impl"
241 WHITESPACE@[242; 243) " "
242 PATH_TYPE@[243; 244)
243 PATH@[243; 244)
244 PATH_SEGMENT@[243; 244)
245 NAME_REF@[243; 244)
246 IDENT@[243; 244) "S"
247 WHITESPACE@[244; 245) " "
248 ITEM_LIST@[245; 519)
249 L_CURLY@[245; 246) "{"
250 WHITESPACE@[246; 252) "\n "
251 FN_DEF@[252; 277)
252 FN_KW@[252; 254) "fn"
253 WHITESPACE@[254; 255) " "
254 NAME@[255; 256)
255 IDENT@[255; 256) "f"
256 PARAM_LIST@[256; 274)
257 L_PAREN@[256; 257) "("
258 ATTR@[257; 268)
259 POUND@[257; 258) "#"
260 TOKEN_TREE@[258; 268)
261 L_BRACK@[258; 259) "["
262 IDENT@[259; 267) "must_use"
263 R_BRACK@[267; 268) "]"
264 WHITESPACE@[268; 269) " "
265 SELF_PARAM@[269; 273)
266 SELF_KW@[269; 273) "self"
267 R_PAREN@[273; 274) ")"
268 WHITESPACE@[274; 275) " "
269 BLOCK@[275; 277)
270 L_CURLY@[275; 276) "{"
271 R_CURLY@[276; 277) "}"
272 WHITESPACE@[277; 283) "\n "
273 FN_DEF@[283; 305)
274 FN_KW@[283; 285) "fn"
275 WHITESPACE@[285; 286) " "
276 NAME@[286; 288)
277 IDENT@[286; 288) "g1"
278 PARAM_LIST@[288; 302)
279 L_PAREN@[288; 289) "("
280 ATTR@[289; 296)
281 POUND@[289; 290) "#"
282 TOKEN_TREE@[290; 296)
283 L_BRACK@[290; 291) "["
284 IDENT@[291; 295) "attr"
285 R_BRACK@[295; 296) "]"
286 WHITESPACE@[296; 297) " "
287 SELF_PARAM@[297; 301)
288 SELF_KW@[297; 301) "self"
289 R_PAREN@[301; 302) ")"
290 WHITESPACE@[302; 303) " "
291 BLOCK@[303; 305)
292 L_CURLY@[303; 304) "{"
293 R_CURLY@[304; 305) "}"
294 WHITESPACE@[305; 311) "\n "
295 FN_DEF@[311; 334)
296 FN_KW@[311; 313) "fn"
297 WHITESPACE@[313; 314) " "
298 NAME@[314; 316)
299 IDENT@[314; 316) "g2"
300 PARAM_LIST@[316; 331)
301 L_PAREN@[316; 317) "("
302 ATTR@[317; 324)
303 POUND@[317; 318) "#"
304 TOKEN_TREE@[318; 324)
305 L_BRACK@[318; 319) "["
306 IDENT@[319; 323) "attr"
307 R_BRACK@[323; 324) "]"
308 WHITESPACE@[324; 325) " "
309 SELF_PARAM@[325; 330)
310 AMP@[325; 326) "&"
311 SELF_KW@[326; 330) "self"
312 R_PAREN@[330; 331) ")"
313 WHITESPACE@[331; 332) " "
314 BLOCK@[332; 334)
315 L_CURLY@[332; 333) "{"
316 R_CURLY@[333; 334) "}"
317 WHITESPACE@[334; 340) "\n "
318 FN_DEF@[340; 371)
319 FN_KW@[340; 342) "fn"
320 WHITESPACE@[342; 343) " "
321 NAME@[343; 345)
322 IDENT@[343; 345) "g3"
323 TYPE_PARAM_LIST@[345; 349)
324 L_ANGLE@[345; 346) "<"
325 LIFETIME_PARAM@[346; 348)
326 LIFETIME@[346; 348) "\'a"
327 R_ANGLE@[348; 349) ">"
328 PARAM_LIST@[349; 368)
329 L_PAREN@[349; 350) "("
330 ATTR@[350; 357)
331 POUND@[350; 351) "#"
332 TOKEN_TREE@[351; 357)
333 L_BRACK@[351; 352) "["
334 IDENT@[352; 356) "attr"
335 R_BRACK@[356; 357) "]"
336 WHITESPACE@[357; 358) " "
337 SELF_PARAM@[358; 367)
338 AMP@[358; 359) "&"
339 MUT_KW@[359; 362) "mut"
340 WHITESPACE@[362; 363) " "
341 SELF_KW@[363; 367) "self"
342 R_PAREN@[367; 368) ")"
343 WHITESPACE@[368; 369) " "
344 BLOCK@[369; 371)
345 L_CURLY@[369; 370) "{"
346 R_CURLY@[370; 371) "}"
347 WHITESPACE@[371; 377) "\n "
348 FN_DEF@[377; 407)
349 FN_KW@[377; 379) "fn"
350 WHITESPACE@[379; 380) " "
351 NAME@[380; 382)
352 IDENT@[380; 382) "g4"
353 TYPE_PARAM_LIST@[382; 386)
354 L_ANGLE@[382; 383) "<"
355 LIFETIME_PARAM@[383; 385)
356 LIFETIME@[383; 385) "\'a"
357 R_ANGLE@[385; 386) ">"
358 PARAM_LIST@[386; 404)
359 L_PAREN@[386; 387) "("
360 ATTR@[387; 394)
361 POUND@[387; 388) "#"
362 TOKEN_TREE@[388; 394)
363 L_BRACK@[388; 389) "["
364 IDENT@[389; 393) "attr"
365 R_BRACK@[393; 394) "]"
366 WHITESPACE@[394; 395) " "
367 SELF_PARAM@[395; 403)
368 AMP@[395; 396) "&"
369 LIFETIME@[396; 398) "\'a"
370 WHITESPACE@[398; 399) " "
371 SELF_KW@[399; 403) "self"
372 R_PAREN@[403; 404) ")"
373 WHITESPACE@[404; 405) " "
374 BLOCK@[405; 407)
375 L_CURLY@[405; 406) "{"
376 R_CURLY@[406; 407) "}"
377 WHITESPACE@[407; 413) "\n "
378 FN_DEF@[413; 447)
379 FN_KW@[413; 415) "fn"
380 WHITESPACE@[415; 416) " "
381 NAME@[416; 418)
382 IDENT@[416; 418) "g5"
383 TYPE_PARAM_LIST@[418; 422)
384 L_ANGLE@[418; 419) "<"
385 LIFETIME_PARAM@[419; 421)
386 LIFETIME@[419; 421) "\'a"
387 R_ANGLE@[421; 422) ">"
388 PARAM_LIST@[422; 444)
389 L_PAREN@[422; 423) "("
390 ATTR@[423; 430)
391 POUND@[423; 424) "#"
392 TOKEN_TREE@[424; 430)
393 L_BRACK@[424; 425) "["
394 IDENT@[425; 429) "attr"
395 R_BRACK@[429; 430) "]"
396 WHITESPACE@[430; 431) " "
397 SELF_PARAM@[431; 443)
398 AMP@[431; 432) "&"
399 LIFETIME@[432; 434) "\'a"
400 WHITESPACE@[434; 435) " "
401 MUT_KW@[435; 438) "mut"
402 WHITESPACE@[438; 439) " "
403 SELF_KW@[439; 443) "self"
404 R_PAREN@[443; 444) ")"
405 WHITESPACE@[444; 445) " "
406 BLOCK@[445; 447)
407 L_CURLY@[445; 446) "{"
408 R_CURLY@[446; 447) "}"
409 WHITESPACE@[447; 453) "\n "
410 FN_DEF@[453; 480)
411 FN_KW@[453; 455) "fn"
412 WHITESPACE@[455; 456) " "
413 NAME@[456; 457)
414 IDENT@[456; 457) "c"
415 PARAM_LIST@[457; 477)
416 L_PAREN@[457; 458) "("
417 ATTR@[458; 465)
418 POUND@[458; 459) "#"
419 TOKEN_TREE@[459; 465)
420 L_BRACK@[459; 460) "["
421 IDENT@[460; 464) "attr"
422 R_BRACK@[464; 465) "]"
423 WHITESPACE@[465; 466) " "
424 SELF_PARAM@[466; 476)
425 SELF_KW@[466; 470) "self"
426 COLON@[470; 471) ":"
427 WHITESPACE@[471; 472) " "
428 PATH_TYPE@[472; 476)
429 PATH@[472; 476)
430 PATH_SEGMENT@[472; 476)
431 NAME_REF@[472; 476)
432 IDENT@[472; 476) "Self"
433 R_PAREN@[476; 477) ")"
434 WHITESPACE@[477; 478) " "
435 BLOCK@[478; 480)
436 L_CURLY@[478; 479) "{"
437 R_CURLY@[479; 480) "}"
438 WHITESPACE@[480; 486) "\n "
439 FN_DEF@[486; 517)
440 FN_KW@[486; 488) "fn"
441 WHITESPACE@[488; 489) " "
442 NAME@[489; 490)
443 IDENT@[489; 490) "d"
444 PARAM_LIST@[490; 514)
445 L_PAREN@[490; 491) "("
446 ATTR@[491; 498)
447 POUND@[491; 492) "#"
448 TOKEN_TREE@[492; 498)
449 L_BRACK@[492; 493) "["
450 IDENT@[493; 497) "attr"
451 R_BRACK@[497; 498) "]"
452 WHITESPACE@[498; 499) " "
453 SELF_PARAM@[499; 513)
454 SELF_KW@[499; 503) "self"
455 COLON@[503; 504) ":"
456 WHITESPACE@[504; 505) " "
457 PATH_TYPE@[505; 513)
458 PATH@[505; 513)
459 PATH_SEGMENT@[505; 513)
460 NAME_REF@[505; 507)
461 IDENT@[505; 507) "Rc"
462 TYPE_ARG_LIST@[507; 513)
463 L_ANGLE@[507; 508) "<"
464 TYPE_ARG@[508; 512)
465 PATH_TYPE@[508; 512)
466 PATH@[508; 512)
467 PATH_SEGMENT@[508; 512)
468 NAME_REF@[508; 512)
469 IDENT@[508; 512) "Self"
470 R_ANGLE@[512; 513) ">"
471 R_PAREN@[513; 514) ")"
472 WHITESPACE@[514; 515) " "
473 BLOCK@[515; 517)
474 L_CURLY@[515; 516) "{"
475 R_CURLY@[516; 517) "}"
476 WHITESPACE@[517; 518) "\n"
477 R_CURLY@[518; 519) "}"
diff --git a/crates/ra_tools/src/main.rs b/crates/ra_tools/src/main.rs
index 4fcb2adeb..54d96e446 100644
--- a/crates/ra_tools/src/main.rs
+++ b/crates/ra_tools/src/main.rs
@@ -63,12 +63,12 @@ fn install(opts: InstallOpt) -> Result<()> {
63 if cfg!(target_os = "macos") { 63 if cfg!(target_os = "macos") {
64 fix_path_for_mac()? 64 fix_path_for_mac()?
65 } 65 }
66 if let Some(client) = opts.client {
67 install_client(client)?;
68 }
69 if let Some(server) = opts.server { 66 if let Some(server) = opts.server {
70 install_server(server)?; 67 install_server(server)?;
71 } 68 }
69 if let Some(client) = opts.client {
70 install_client(client)?;
71 }
72 Ok(()) 72 Ok(())
73} 73}
74 74
diff --git a/crates/ra_vfs_glob/Cargo.toml b/crates/ra_vfs_glob/Cargo.toml
new file mode 100644
index 000000000..0390d7da1
--- /dev/null
+++ b/crates/ra_vfs_glob/Cargo.toml
@@ -0,0 +1,9 @@
1[package]
2edition = "2018"
3name = "ra_vfs_glob"
4version = "0.1.0"
5authors = ["rust-analyzer developers"]
6
7[dependencies]
8ra_vfs = "0.2.0"
9globset = "0.4.4"
diff --git a/crates/ra_vfs_glob/src/lib.rs b/crates/ra_vfs_glob/src/lib.rs
new file mode 100644
index 000000000..12401d75a
--- /dev/null
+++ b/crates/ra_vfs_glob/src/lib.rs
@@ -0,0 +1,94 @@
1//! `ra_vfs_glob` crate implements exclusion rules for vfs.
2//!
3//! By default, we include only `.rs` files, and skip some know offenders like
4//! `/target` or `/node_modules` altogether.
5//!
6//! It's also possible to add custom exclusion globs.
7
8use globset::{GlobSet, GlobSetBuilder};
9use ra_vfs::{Filter, RelativePath};
10
11pub use globset::{Glob, GlobBuilder};
12
13const ALWAYS_IGNORED: &[&str] = &["target/**", "**/node_modules/**", "**/.git/**"];
14const IGNORED_FOR_NON_MEMBERS: &[&str] = &["examples/**", "tests/**", "benches/**"];
15
16pub struct RustPackageFilterBuilder {
17 is_member: bool,
18 exclude: GlobSetBuilder,
19}
20
21impl Default for RustPackageFilterBuilder {
22 fn default() -> RustPackageFilterBuilder {
23 RustPackageFilterBuilder { is_member: false, exclude: GlobSetBuilder::new() }
24 }
25}
26
27impl RustPackageFilterBuilder {
28 pub fn set_member(mut self, is_member: bool) -> RustPackageFilterBuilder {
29 self.is_member = is_member;
30 self
31 }
32 pub fn exclude(mut self, glob: Glob) -> RustPackageFilterBuilder {
33 self.exclude.add(glob);
34 self
35 }
36 pub fn into_vfs_filter(self) -> Box<dyn Filter> {
37 let RustPackageFilterBuilder { is_member, mut exclude } = self;
38 for &glob in ALWAYS_IGNORED {
39 exclude.add(Glob::new(glob).unwrap());
40 }
41 if !is_member {
42 for &glob in IGNORED_FOR_NON_MEMBERS {
43 exclude.add(Glob::new(glob).unwrap());
44 }
45 }
46 Box::new(RustPackageFilter { exclude: exclude.build().unwrap() })
47 }
48}
49
50struct RustPackageFilter {
51 exclude: GlobSet,
52}
53
54impl Filter for RustPackageFilter {
55 fn include_dir(&self, dir_path: &RelativePath) -> bool {
56 !self.exclude.is_match(dir_path.as_str())
57 }
58
59 fn include_file(&self, file_path: &RelativePath) -> bool {
60 file_path.extension() == Some("rs")
61 }
62}
63
64#[test]
65fn test_globs() {
66 let filter = RustPackageFilterBuilder::default().set_member(true).into_vfs_filter();
67
68 assert!(filter.include_dir(RelativePath::new("src/tests")));
69 assert!(filter.include_dir(RelativePath::new("src/target")));
70 assert!(filter.include_dir(RelativePath::new("tests")));
71 assert!(filter.include_dir(RelativePath::new("benches")));
72
73 assert!(!filter.include_dir(RelativePath::new("target")));
74 assert!(!filter.include_dir(RelativePath::new("src/foo/.git")));
75 assert!(!filter.include_dir(RelativePath::new("foo/node_modules")));
76
77 let filter = RustPackageFilterBuilder::default().set_member(false).into_vfs_filter();
78
79 assert!(filter.include_dir(RelativePath::new("src/tests")));
80 assert!(filter.include_dir(RelativePath::new("src/target")));
81
82 assert!(!filter.include_dir(RelativePath::new("target")));
83 assert!(!filter.include_dir(RelativePath::new("src/foo/.git")));
84 assert!(!filter.include_dir(RelativePath::new("foo/node_modules")));
85 assert!(!filter.include_dir(RelativePath::new("tests")));
86 assert!(!filter.include_dir(RelativePath::new("benches")));
87
88 let filter = RustPackageFilterBuilder::default()
89 .set_member(true)
90 .exclude(Glob::new("src/llvm-project/**").unwrap())
91 .into_vfs_filter();
92
93 assert!(!filter.include_dir(RelativePath::new("src/llvm-project/clang")));
94}