aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/utils.rs110
-rw-r--r--crates/hir/src/code_model.rs37
-rw-r--r--crates/hir/src/lib.rs2
-rw-r--r--crates/hir_expand/src/name.rs1
-rw-r--r--crates/ide/src/inlay_hints.rs201
-rw-r--r--crates/mbe/src/subtree_source.rs30
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs11
-rw-r--r--crates/rust-analyzer/src/main_loop.rs2
-rw-r--r--crates/syntax/src/parsing/lexer.rs23
-rw-r--r--crates/syntax/src/validation.rs45
-rw-r--r--crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast192
-rw-r--r--crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rs6
-rw-r--r--crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast200
-rw-r--r--crates/syntax/test_data/parser/ok/0069_multi_trait_object.rs6
-rw-r--r--docs/dev/style.md388
-rw-r--r--docs/user/manual.adoc17
16 files changed, 1020 insertions, 251 deletions
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index eb69c49a4..c1847f601 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -3,7 +3,7 @@ pub(crate) mod insert_use;
3 3
4use std::{iter, ops}; 4use std::{iter, ops};
5 5
6use hir::{Adt, Crate, Enum, ScopeDef, Semantics, Trait, Type}; 6use hir::{Adt, Crate, Enum, Module, ScopeDef, Semantics, Trait, Type};
7use ide_db::RootDatabase; 7use ide_db::RootDatabase;
8use itertools::Itertools; 8use itertools::Itertools;
9use rustc_hash::FxHashSet; 9use rustc_hash::FxHashSet;
@@ -274,15 +274,79 @@ impl TryEnum {
274/// somewhat similar to the known paths infra inside hir, but it different; We 274/// somewhat similar to the known paths infra inside hir, but it different; We
275/// want to make sure that IDE specific paths don't become interesting inside 275/// want to make sure that IDE specific paths don't become interesting inside
276/// the compiler itself as well. 276/// the compiler itself as well.
277pub(crate) struct FamousDefs<'a, 'b>(pub(crate) &'a Semantics<'b, RootDatabase>, pub(crate) Crate); 277pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Crate);
278 278
279#[allow(non_snake_case)] 279#[allow(non_snake_case)]
280impl FamousDefs<'_, '_> { 280impl FamousDefs<'_, '_> {
281 #[cfg(test)] 281 pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core
282 pub(crate) const FIXTURE: &'static str = r#"//- /libcore.rs crate:core
283pub mod convert { 282pub mod convert {
284 pub trait From<T> { 283 pub trait From<T> {
285 fn from(T) -> Self; 284 fn from(t: T) -> Self;
285 }
286}
287
288pub mod iter {
289 pub use self::traits::{collect::IntoIterator, iterator::Iterator};
290 mod traits {
291 pub(crate) mod iterator {
292 use crate::option::Option;
293 pub trait Iterator {
294 type Item;
295 fn next(&mut self) -> Option<Self::Item>;
296 fn by_ref(&mut self) -> &mut Self {
297 self
298 }
299 fn take(self, n: usize) -> crate::iter::Take<Self> {
300 crate::iter::Take { inner: self }
301 }
302 }
303
304 impl<I: Iterator> Iterator for &mut I {
305 type Item = I::Item;
306 fn next(&mut self) -> Option<I::Item> {
307 (**self).next()
308 }
309 }
310 }
311 pub(crate) mod collect {
312 pub trait IntoIterator {
313 type Item;
314 }
315 }
316 }
317
318 pub use self::sources::*;
319 pub(crate) mod sources {
320 use super::Iterator;
321 use crate::option::Option::{self, *};
322 pub struct Repeat<A> {
323 element: A,
324 }
325
326 pub fn repeat<T>(elt: T) -> Repeat<T> {
327 Repeat { element: elt }
328 }
329
330 impl<A> Iterator for Repeat<A> {
331 type Item = A;
332
333 fn next(&mut self) -> Option<A> {
334 None
335 }
336 }
337 }
338
339 pub use self::adapters::*;
340 pub(crate) mod adapters {
341 use super::Iterator;
342 use crate::option::Option::{self, *};
343 pub struct Take<I> { pub(crate) inner: I }
344 impl<I> Iterator for Take<I> where I: Iterator {
345 type Item = <I as Iterator>::Item;
346 fn next(&mut self) -> Option<<I as Iterator>::Item> {
347 None
348 }
349 }
286 } 350 }
287} 351}
288 352
@@ -291,7 +355,7 @@ pub mod option {
291} 355}
292 356
293pub mod prelude { 357pub mod prelude {
294 pub use crate::{convert::From, option::Option::{self, *}}; 358 pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}};
295} 359}
296#[prelude_import] 360#[prelude_import]
297pub use prelude::*; 361pub use prelude::*;
@@ -305,6 +369,14 @@ pub use prelude::*;
305 self.find_enum("core:option:Option") 369 self.find_enum("core:option:Option")
306 } 370 }
307 371
372 pub fn core_iter_Iterator(&self) -> Option<Trait> {
373 self.find_trait("core:iter:traits:iterator:Iterator")
374 }
375
376 pub fn core_iter(&self) -> Option<Module> {
377 self.find_module("core:iter")
378 }
379
308 fn find_trait(&self, path: &str) -> Option<Trait> { 380 fn find_trait(&self, path: &str) -> Option<Trait> {
309 match self.find_def(path)? { 381 match self.find_def(path)? {
310 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), 382 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
@@ -319,23 +391,33 @@ pub use prelude::*;
319 } 391 }
320 } 392 }
321 393
394 fn find_module(&self, path: &str) -> Option<Module> {
395 match self.find_def(path)? {
396 hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(it)) => Some(it),
397 _ => None,
398 }
399 }
400
322 fn find_def(&self, path: &str) -> Option<ScopeDef> { 401 fn find_def(&self, path: &str) -> Option<ScopeDef> {
323 let db = self.0.db; 402 let db = self.0.db;
324 let mut path = path.split(':'); 403 let mut path = path.split(':');
325 let trait_ = path.next_back()?; 404 let trait_ = path.next_back()?;
326 let std_crate = path.next()?; 405 let std_crate = path.next()?;
327 let std_crate = self 406 let std_crate = if self
328 .1 407 .1
329 .dependencies(db) 408 .declaration_name(db)
330 .into_iter() 409 .map(|name| name.to_string() == std_crate)
331 .find(|dep| &dep.name.to_string() == std_crate)? 410 .unwrap_or(false)
332 .krate; 411 {
333 412 self.1
413 } else {
414 self.1.dependencies(db).into_iter().find(|dep| dep.name.to_string() == std_crate)?.krate
415 };
334 let mut module = std_crate.root_module(db); 416 let mut module = std_crate.root_module(db);
335 for segment in path { 417 for segment in path {
336 module = module.children(db).find_map(|child| { 418 module = module.children(db).find_map(|child| {
337 let name = child.name(db)?; 419 let name = child.name(db)?;
338 if &name.to_string() == segment { 420 if name.to_string() == segment {
339 Some(child) 421 Some(child)
340 } else { 422 } else {
341 None 423 None
@@ -343,7 +425,7 @@ pub use prelude::*;
343 })?; 425 })?;
344 } 426 }
345 let def = 427 let def =
346 module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1; 428 module.scope(db, None).into_iter().find(|(name, _def)| name.to_string() == trait_)?.1;
347 Some(def) 429 Some(def)
348 } 430 }
349} 431}
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index c75d46bff..031c91ccf 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -30,8 +30,12 @@ use hir_expand::{
30use hir_ty::{ 30use hir_ty::{
31 autoderef, 31 autoderef,
32 display::{HirDisplayError, HirFormatter}, 32 display::{HirDisplayError, HirFormatter},
33 method_resolution, ApplicationTy, CallableDefId, Canonical, FnSig, GenericPredicate, 33 method_resolution,
34 InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, 34 traits::Solution,
35 traits::SolutionVariables,
36 ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate,
37 InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, Ty,
38 TyDefId, TyKind, TypeCtor,
35}; 39};
36use rustc_hash::FxHashSet; 40use rustc_hash::FxHashSet;
37use stdx::impl_from; 41use stdx::impl_from;
@@ -1362,6 +1366,35 @@ impl Type {
1362 db.trait_solve(self.krate, goal).is_some() 1366 db.trait_solve(self.krate, goal).is_some()
1363 } 1367 }
1364 1368
1369 pub fn normalize_trait_assoc_type(
1370 &self,
1371 db: &dyn HirDatabase,
1372 r#trait: Trait,
1373 args: &[Type],
1374 alias: TypeAlias,
1375 ) -> Option<Ty> {
1376 let subst = Substs::build_for_def(db, r#trait.id)
1377 .push(self.ty.value.clone())
1378 .fill(args.iter().map(|t| t.ty.value.clone()))
1379 .build();
1380 let predicate = ProjectionPredicate {
1381 projection_ty: ProjectionTy { associated_ty: alias.id, parameters: subst },
1382 ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)),
1383 };
1384 let goal = Canonical {
1385 value: InEnvironment::new(
1386 self.ty.environment.clone(),
1387 Obligation::Projection(predicate),
1388 ),
1389 kinds: Arc::new([TyKind::General]),
1390 };
1391
1392 match db.trait_solve(self.krate, goal)? {
1393 Solution::Unique(SolutionVariables(subst)) => subst.value.first().cloned(),
1394 Solution::Ambig(_) => None,
1395 }
1396 }
1397
1365 pub fn is_copy(&self, db: &dyn HirDatabase) -> bool { 1398 pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
1366 let lang_item = db.lang_item(self.krate, SmolStr::new("copy")); 1399 let lang_item = db.lang_item(self.krate, SmolStr::new("copy"));
1367 let copy_trait = match lang_item { 1400 let copy_trait = match lang_item {
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 171118d98..4094a76cb 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -55,7 +55,7 @@ pub use hir_def::{
55 type_ref::{Mutability, TypeRef}, 55 type_ref::{Mutability, TypeRef},
56}; 56};
57pub use hir_expand::{ 57pub use hir_expand::{
58 name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, 58 name::known, name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc,
59 /* FIXME */ MacroDefId, MacroFile, Origin, 59 /* FIXME */ MacroDefId, MacroFile, Origin,
60}; 60};
61pub use hir_ty::display::HirDisplay; 61pub use hir_ty::display::HirDisplay;
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index a5750d829..63f828707 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -164,6 +164,7 @@ pub mod known {
164 result, 164 result,
165 boxed, 165 boxed,
166 // Components of known path (type name) 166 // Components of known path (type name)
167 Iterator,
167 IntoIterator, 168 IntoIterator,
168 Item, 169 Item,
169 Try, 170 Try,
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 3a4dc6a84..7d716577e 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1,4 +1,5 @@
1use hir::{Adt, Callable, HirDisplay, Semantics, Type}; 1use assists::utils::FamousDefs;
2use hir::{known, HirDisplay, Semantics};
2use ide_db::RootDatabase; 3use ide_db::RootDatabase;
3use stdx::to_lower_snake_case; 4use stdx::to_lower_snake_case;
4use syntax::{ 5use syntax::{
@@ -119,17 +120,18 @@ fn get_chaining_hints(
119 return None; 120 return None;
120 } 121 }
121 if matches!(expr, ast::Expr::PathExpr(_)) { 122 if matches!(expr, ast::Expr::PathExpr(_)) {
122 if let Some(Adt::Struct(st)) = ty.as_adt() { 123 if let Some(hir::Adt::Struct(st)) = ty.as_adt() {
123 if st.fields(sema.db).is_empty() { 124 if st.fields(sema.db).is_empty() {
124 return None; 125 return None;
125 } 126 }
126 } 127 }
127 } 128 }
128 let label = ty.display_truncated(sema.db, config.max_length).to_string();
129 acc.push(InlayHint { 129 acc.push(InlayHint {
130 range: expr.syntax().text_range(), 130 range: expr.syntax().text_range(),
131 kind: InlayKind::ChainingHint, 131 kind: InlayKind::ChainingHint,
132 label: label.into(), 132 label: hint_iterator(sema, config, &ty).unwrap_or_else(|| {
133 ty.display_truncated(sema.db, config.max_length).to_string().into()
134 }),
133 }); 135 });
134 } 136 }
135 Some(()) 137 Some(())
@@ -192,17 +194,58 @@ fn get_bind_pat_hints(
192 if should_not_display_type_hint(sema, &pat, &ty) { 194 if should_not_display_type_hint(sema, &pat, &ty) {
193 return None; 195 return None;
194 } 196 }
195
196 acc.push(InlayHint { 197 acc.push(InlayHint {
197 range: pat.syntax().text_range(), 198 range: pat.syntax().text_range(),
198 kind: InlayKind::TypeHint, 199 kind: InlayKind::TypeHint,
199 label: ty.display_truncated(sema.db, config.max_length).to_string().into(), 200 label: hint_iterator(sema, config, &ty)
201 .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string().into()),
200 }); 202 });
203
201 Some(()) 204 Some(())
202} 205}
203 206
204fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Type) -> bool { 207/// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`.
205 if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() { 208fn hint_iterator(
209 sema: &Semantics<RootDatabase>,
210 config: &InlayHintsConfig,
211 ty: &hir::Type,
212) -> Option<SmolStr> {
213 let db = sema.db;
214 let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref())
215 .last()
216 .and_then(|strukt| strukt.as_adt())?;
217 let krate = strukt.krate(db)?;
218 if krate.declaration_name(db).as_deref() != Some("core") {
219 return None;
220 }
221 let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
222 let iter_mod = FamousDefs(sema, krate).core_iter()?;
223 // assert this type comes from `core::iter`
224 iter_mod.visibility_of(db, &iter_trait.into()).filter(|&vis| vis == hir::Visibility::Public)?;
225 if ty.impls_trait(db, iter_trait, &[]) {
226 let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item {
227 hir::AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias),
228 _ => None,
229 })?;
230 if let Some(ty) = ty.normalize_trait_assoc_type(db, iter_trait, &[], assoc_type_item) {
231 const LABEL_START: &str = "impl Iterator<Item = ";
232 const LABEL_END: &str = ">";
233
234 let ty_display = ty.display_truncated(
235 db,
236 config
237 .max_length
238 .map(|len| len.saturating_sub(LABEL_START.len() + LABEL_END.len())),
239 );
240 return Some(format!("{}{}{}", LABEL_START, ty_display, LABEL_END).into());
241 }
242 }
243
244 None
245}
246
247fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool {
248 if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() {
206 let pat_text = bind_pat.to_string(); 249 let pat_text = bind_pat.to_string();
207 enum_data 250 enum_data
208 .variants(db) 251 .variants(db)
@@ -217,7 +260,7 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Typ
217fn should_not_display_type_hint( 260fn should_not_display_type_hint(
218 sema: &Semantics<RootDatabase>, 261 sema: &Semantics<RootDatabase>,
219 bind_pat: &ast::IdentPat, 262 bind_pat: &ast::IdentPat,
220 pat_ty: &Type, 263 pat_ty: &hir::Type,
221) -> bool { 264) -> bool {
222 let db = sema.db; 265 let db = sema.db;
223 266
@@ -225,7 +268,7 @@ fn should_not_display_type_hint(
225 return true; 268 return true;
226 } 269 }
227 270
228 if let Some(Adt::Struct(s)) = pat_ty.as_adt() { 271 if let Some(hir::Adt::Struct(s)) = pat_ty.as_adt() {
229 if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.to_string() { 272 if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.to_string() {
230 return true; 273 return true;
231 } 274 }
@@ -269,7 +312,7 @@ fn should_not_display_type_hint(
269 312
270fn should_show_param_name_hint( 313fn should_show_param_name_hint(
271 sema: &Semantics<RootDatabase>, 314 sema: &Semantics<RootDatabase>,
272 callable: &Callable, 315 callable: &hir::Callable,
273 param_name: &str, 316 param_name: &str,
274 argument: &ast::Expr, 317 argument: &ast::Expr,
275) -> bool { 318) -> bool {
@@ -316,7 +359,7 @@ fn is_enum_name_similar_to_param_name(
316 param_name: &str, 359 param_name: &str,
317) -> bool { 360) -> bool {
318 match sema.type_of_expr(argument).and_then(|t| t.as_adt()) { 361 match sema.type_of_expr(argument).and_then(|t| t.as_adt()) {
319 Some(Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name, 362 Some(hir::Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name,
320 _ => false, 363 _ => false,
321 } 364 }
322} 365}
@@ -337,7 +380,7 @@ fn is_obvious_param(param_name: &str) -> bool {
337 param_name.len() == 1 || is_obvious_param_name 380 param_name.len() == 1 || is_obvious_param_name
338} 381}
339 382
340fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<Callable> { 383fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Callable> {
341 match expr { 384 match expr {
342 ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db), 385 ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db),
343 ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr), 386 ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr),
@@ -347,6 +390,7 @@ fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<Call
347 390
348#[cfg(test)] 391#[cfg(test)]
349mod tests { 392mod tests {
393 use assists::utils::FamousDefs;
350 use expect_test::{expect, Expect}; 394 use expect_test::{expect, Expect};
351 use test_utils::extract_annotations; 395 use test_utils::extract_annotations;
352 396
@@ -357,7 +401,9 @@ mod tests {
357 } 401 }
358 402
359 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { 403 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
360 let (analysis, file_id) = fixture::file(ra_fixture); 404 let ra_fixture =
405 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
406 let (analysis, file_id) = fixture::file(&ra_fixture);
361 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); 407 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
362 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); 408 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
363 let actual = 409 let actual =
@@ -366,7 +412,9 @@ mod tests {
366 } 412 }
367 413
368 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { 414 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
369 let (analysis, file_id) = fixture::file(ra_fixture); 415 let ra_fixture =
416 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
417 let (analysis, file_id) = fixture::file(&ra_fixture);
370 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); 418 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
371 expect.assert_debug_eq(&inlay_hints) 419 expect.assert_debug_eq(&inlay_hints)
372 } 420 }
@@ -798,12 +846,12 @@ fn main() {
798 expect![[r#" 846 expect![[r#"
799 [ 847 [
800 InlayHint { 848 InlayHint {
801 range: 147..172, 849 range: 148..173,
802 kind: ChainingHint, 850 kind: ChainingHint,
803 label: "B", 851 label: "B",
804 }, 852 },
805 InlayHint { 853 InlayHint {
806 range: 147..154, 854 range: 148..155,
807 kind: ChainingHint, 855 kind: ChainingHint,
808 label: "A", 856 label: "A",
809 }, 857 },
@@ -864,12 +912,12 @@ fn main() {
864 expect![[r#" 912 expect![[r#"
865 [ 913 [
866 InlayHint { 914 InlayHint {
867 range: 143..190, 915 range: 144..191,
868 kind: ChainingHint, 916 kind: ChainingHint,
869 label: "C", 917 label: "C",
870 }, 918 },
871 InlayHint { 919 InlayHint {
872 range: 143..179, 920 range: 144..180,
873 kind: ChainingHint, 921 kind: ChainingHint,
874 label: "B", 922 label: "B",
875 }, 923 },
@@ -909,12 +957,12 @@ fn main() {
909 expect![[r#" 957 expect![[r#"
910 [ 958 [
911 InlayHint { 959 InlayHint {
912 range: 246..283, 960 range: 247..284,
913 kind: ChainingHint, 961 kind: ChainingHint,
914 label: "B<X<i32, bool>>", 962 label: "B<X<i32, bool>>",
915 }, 963 },
916 InlayHint { 964 InlayHint {
917 range: 246..265, 965 range: 247..266,
918 kind: ChainingHint, 966 kind: ChainingHint,
919 label: "A<X<i32, bool>>", 967 label: "A<X<i32, bool>>",
920 }, 968 },
@@ -935,7 +983,6 @@ fn main() {
935 ); 983 );
936 check( 984 check(
937 r#" 985 r#"
938//- /main.rs crate:main deps:core
939pub struct Vec<T> {} 986pub struct Vec<T> {}
940 987
941impl<T> Vec<T> { 988impl<T> Vec<T> {
@@ -956,13 +1003,6 @@ fn main() {
956 println!("Unit expr"); 1003 println!("Unit expr");
957} 1004}
958 1005
959//- /core.rs crate:core
960#[prelude_import] use iter::*;
961mod iter {
962 trait IntoIterator {
963 type Item;
964 }
965}
966//- /alloc.rs crate:alloc deps:core 1006//- /alloc.rs crate:alloc deps:core
967mod collections { 1007mod collections {
968 struct Vec<T> {} 1008 struct Vec<T> {}
@@ -982,7 +1022,6 @@ mod collections {
982 fn complete_for_hint() { 1022 fn complete_for_hint() {
983 check( 1023 check(
984 r#" 1024 r#"
985//- /main.rs crate:main deps:core
986pub struct Vec<T> {} 1025pub struct Vec<T> {}
987 1026
988impl<T> Vec<T> { 1027impl<T> Vec<T> {
@@ -1004,14 +1043,6 @@ fn main() {
1004 //^ &str 1043 //^ &str
1005 } 1044 }
1006} 1045}
1007
1008//- /core.rs crate:core
1009#[prelude_import] use iter::*;
1010mod iter {
1011 trait IntoIterator {
1012 type Item;
1013 }
1014}
1015//- /alloc.rs crate:alloc deps:core 1046//- /alloc.rs crate:alloc deps:core
1016mod collections { 1047mod collections {
1017 struct Vec<T> {} 1048 struct Vec<T> {}
@@ -1037,7 +1068,6 @@ mod collections {
1037 max_length: None, 1068 max_length: None,
1038 }, 1069 },
1039 r#" 1070 r#"
1040//- /main.rs crate:main
1041pub struct Vec<T> {} 1071pub struct Vec<T> {}
1042 1072
1043impl<T> Vec<T> { 1073impl<T> Vec<T> {
@@ -1060,4 +1090,97 @@ fn main() {
1060"#, 1090"#,
1061 ); 1091 );
1062 } 1092 }
1093
1094 #[test]
1095 fn shorten_iterator_hints() {
1096 check_with_config(
1097 InlayHintsConfig {
1098 parameter_hints: false,
1099 type_hints: true,
1100 chaining_hints: false,
1101 max_length: None,
1102 },
1103 r#"
1104use core::iter;
1105
1106struct MyIter;
1107
1108impl Iterator for MyIter {
1109 type Item = ();
1110 fn next(&mut self) -> Option<Self::Item> {
1111 None
1112 }
1113}
1114
1115fn main() {
1116 let _x = MyIter;
1117 //^^ MyIter
1118 let _x = iter::repeat(0);
1119 //^^ impl Iterator<Item = i32>
1120 fn generic<T: Clone>(t: T) {
1121 let _x = iter::repeat(t);
1122 //^^ impl Iterator<Item = T>
1123 let _chained = iter::repeat(t).take(10);
1124 //^^^^^^^^ impl Iterator<Item = T>
1125 }
1126}
1127"#,
1128 );
1129 }
1130
1131 #[test]
1132 fn shorten_iterator_chaining_hints() {
1133 check_expect(
1134 InlayHintsConfig {
1135 parameter_hints: false,
1136 type_hints: false,
1137 chaining_hints: true,
1138 max_length: None,
1139 },
1140 r#"
1141use core::iter;
1142
1143struct MyIter;
1144
1145impl Iterator for MyIter {
1146 type Item = ();
1147 fn next(&mut self) -> Option<Self::Item> {
1148 None
1149 }
1150}
1151
1152fn main() {
1153 let _x = MyIter.by_ref()
1154 .take(5)
1155 .by_ref()
1156 .take(5)
1157 .by_ref();
1158}
1159"#,
1160 expect![[r#"
1161 [
1162 InlayHint {
1163 range: 175..242,
1164 kind: ChainingHint,
1165 label: "impl Iterator<Item = ()>",
1166 },
1167 InlayHint {
1168 range: 175..225,
1169 kind: ChainingHint,
1170 label: "impl Iterator<Item = ()>",
1171 },
1172 InlayHint {
1173 range: 175..207,
1174 kind: ChainingHint,
1175 label: "impl Iterator<Item = ()>",
1176 },
1177 InlayHint {
1178 range: 175..190,
1179 kind: ChainingHint,
1180 label: "&mut MyIter",
1181 },
1182 ]
1183 "#]],
1184 );
1185 }
1063} 1186}
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs
index 41461b315..396ce8b16 100644
--- a/crates/mbe/src/subtree_source.rs
+++ b/crates/mbe/src/subtree_source.rs
@@ -155,9 +155,14 @@ fn convert_delim(d: Option<tt::DelimiterKind>, closing: bool) -> TtToken {
155} 155}
156 156
157fn convert_literal(l: &tt::Literal) -> TtToken { 157fn convert_literal(l: &tt::Literal) -> TtToken {
158 let kind = lex_single_syntax_kind(&l.text) 158 let is_negated = l.text.starts_with('-');
159 let inner_text = &l.text[if is_negated { 1 } else { 0 }..];
160
161 let kind = lex_single_syntax_kind(inner_text)
159 .map(|(kind, _error)| kind) 162 .map(|(kind, _error)| kind)
160 .filter(|kind| kind.is_literal()) 163 .filter(|kind| {
164 kind.is_literal() && (!is_negated || matches!(kind, FLOAT_NUMBER | INT_NUMBER))
165 })
161 .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l)); 166 .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l));
162 167
163 TtToken { kind, is_joint_to_next: false, text: l.text.clone() } 168 TtToken { kind, is_joint_to_next: false, text: l.text.clone() }
@@ -195,3 +200,24 @@ fn convert_leaf(leaf: &tt::Leaf) -> TtToken {
195 tt::Leaf::Punct(punct) => convert_punct(*punct), 200 tt::Leaf::Punct(punct) => convert_punct(*punct),
196 } 201 }
197} 202}
203
204#[cfg(test)]
205mod tests {
206 use super::{convert_literal, TtToken};
207 use syntax::{SmolStr, SyntaxKind};
208
209 #[test]
210 fn test_negative_literal() {
211 assert_eq!(
212 convert_literal(&tt::Literal {
213 id: tt::TokenId::unspecified(),
214 text: SmolStr::new("-42.0")
215 }),
216 TtToken {
217 kind: SyntaxKind::FLOAT_NUMBER,
218 is_joint_to_next: false,
219 text: SmolStr::new("-42.0")
220 }
221 );
222 }
223}
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 85c661571..bd888f634 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -25,8 +25,9 @@ pub(crate) enum Progress {
25} 25}
26 26
27impl Progress { 27impl Progress {
28 pub(crate) fn percentage(done: usize, total: usize) -> f64 { 28 pub(crate) fn fraction(done: usize, total: usize) -> f64 {
29 (done as f64 / total.max(1) as f64) * 100.0 29 assert!(done <= total);
30 done as f64 / total.max(1) as f64
30 } 31 }
31} 32}
32 33
@@ -43,11 +44,15 @@ impl GlobalState {
43 title: &str, 44 title: &str,
44 state: Progress, 45 state: Progress,
45 message: Option<String>, 46 message: Option<String>,
46 percentage: Option<f64>, 47 fraction: Option<f64>,
47 ) { 48 ) {
48 if !self.config.client_caps.work_done_progress { 49 if !self.config.client_caps.work_done_progress {
49 return; 50 return;
50 } 51 }
52 let percentage = fraction.map(|f| {
53 assert!(0.0 <= f && f <= 1.0);
54 f * 100.0
55 });
51 let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); 56 let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
52 let work_done_progress = match state { 57 let work_done_progress = match state {
53 Progress::Begin => { 58 Progress::Begin => {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index c2d0ac791..4b7ac8224 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -230,7 +230,7 @@ impl GlobalState {
230 "roots scanned", 230 "roots scanned",
231 state, 231 state,
232 Some(format!("{}/{}", n_done, n_total)), 232 Some(format!("{}/{}", n_done, n_total)),
233 Some(Progress::percentage(n_done, n_total)), 233 Some(Progress::fraction(n_done, n_total)),
234 ) 234 )
235 } 235 }
236 } 236 }
diff --git a/crates/syntax/src/parsing/lexer.rs b/crates/syntax/src/parsing/lexer.rs
index f1202113b..7e38c32cc 100644
--- a/crates/syntax/src/parsing/lexer.rs
+++ b/crates/syntax/src/parsing/lexer.rs
@@ -1,10 +1,10 @@
1//! Lexer analyzes raw input string and produces lexemes (tokens). 1//! Lexer analyzes raw input string and produces lexemes (tokens).
2//! It is just a bridge to `rustc_lexer`. 2//! It is just a bridge to `rustc_lexer`.
3 3
4use rustc_lexer::{LiteralKind as LK, RawStrError};
5
6use std::convert::TryInto; 4use std::convert::TryInto;
7 5
6use rustc_lexer::{LiteralKind as LK, RawStrError};
7
8use crate::{ 8use crate::{
9 SyntaxError, 9 SyntaxError,
10 SyntaxKind::{self, *}, 10 SyntaxKind::{self, *},
@@ -61,17 +61,18 @@ pub fn tokenize(text: &str) -> (Vec<Token>, Vec<SyntaxError>) {
61 (tokens, errors) 61 (tokens, errors)
62} 62}
63 63
64/// Returns `SyntaxKind` and `Option<SyntaxError>` of the first token 64/// Returns `SyntaxKind` and `Option<SyntaxError>` if `text` parses as a single token.
65/// encountered at the beginning of the string.
66/// 65///
67/// Returns `None` if the string contains zero *or two or more* tokens. 66/// Returns `None` if the string contains zero *or two or more* tokens.
68/// The token is malformed if the returned error is not `None`. 67/// The token is malformed if the returned error is not `None`.
69/// 68///
70/// Beware that unescape errors are not checked at tokenization time. 69/// Beware that unescape errors are not checked at tokenization time.
71pub fn lex_single_syntax_kind(text: &str) -> Option<(SyntaxKind, Option<SyntaxError>)> { 70pub fn lex_single_syntax_kind(text: &str) -> Option<(SyntaxKind, Option<SyntaxError>)> {
72 lex_first_token(text) 71 let (first_token, err) = lex_first_token(text)?;
73 .filter(|(token, _)| token.len == TextSize::of(text)) 72 if first_token.len != TextSize::of(text) {
74 .map(|(token, error)| (token.kind, error)) 73 return None;
74 }
75 Some((first_token.kind, err))
75} 76}
76 77
77/// The same as `lex_single_syntax_kind()` but returns only `SyntaxKind` and 78/// The same as `lex_single_syntax_kind()` but returns only `SyntaxKind` and
@@ -79,9 +80,11 @@ pub fn lex_single_syntax_kind(text: &str) -> Option<(SyntaxKind, Option<SyntaxEr
79/// 80///
80/// Beware that unescape errors are not checked at tokenization time. 81/// Beware that unescape errors are not checked at tokenization time.
81pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> { 82pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> {
82 lex_first_token(text) 83 let (single_token, err) = lex_single_syntax_kind(text)?;
83 .filter(|(token, error)| !error.is_some() && token.len == TextSize::of(text)) 84 if err.is_some() {
84 .map(|(token, _error)| token.kind) 85 return None;
86 }
87 Some(single_token)
85} 88}
86 89
87/// Returns `SyntaxKind` and `Option<SyntaxError>` of the first token 90/// Returns `SyntaxKind` and `Option<SyntaxError>` of the first token
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 2dddaf09a..0f9a5e8ae 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -3,10 +3,11 @@
3mod block; 3mod block;
4 4
5use crate::{ 5use crate::{
6 ast, match_ast, AstNode, SyntaxError, 6 algo, ast, match_ast, AstNode, SyntaxError,
7 SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST, FN, INT_NUMBER, STRING, TYPE_ALIAS}, 7 SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST, FN, INT_NUMBER, STRING, TYPE_ALIAS},
8 SyntaxNode, SyntaxToken, TextSize, T, 8 SyntaxNode, SyntaxToken, TextSize, T,
9}; 9};
10use rowan::Direction;
10use rustc_lexer::unescape::{ 11use rustc_lexer::unescape::{
11 self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode, 12 self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode,
12}; 13};
@@ -95,6 +96,9 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
95 ast::Visibility(it) => validate_visibility(it, &mut errors), 96 ast::Visibility(it) => validate_visibility(it, &mut errors),
96 ast::RangeExpr(it) => validate_range_expr(it, &mut errors), 97 ast::RangeExpr(it) => validate_range_expr(it, &mut errors),
97 ast::PathSegment(it) => validate_path_keywords(it, &mut errors), 98 ast::PathSegment(it) => validate_path_keywords(it, &mut errors),
99 ast::RefType(it) => validate_trait_object_ref_ty(it, &mut errors),
100 ast::PtrType(it) => validate_trait_object_ptr_ty(it, &mut errors),
101 ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, &mut errors),
98 _ => (), 102 _ => (),
99 } 103 }
100 } 104 }
@@ -301,3 +305,42 @@ fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxErro
301 return true; 305 return true;
302 } 306 }
303} 307}
308
309fn validate_trait_object_ref_ty(ty: ast::RefType, errors: &mut Vec<SyntaxError>) {
310 if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
311 if let Some(err) = validate_trait_object_ty(ty) {
312 errors.push(err);
313 }
314 }
315}
316
317fn validate_trait_object_ptr_ty(ty: ast::PtrType, errors: &mut Vec<SyntaxError>) {
318 if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
319 if let Some(err) = validate_trait_object_ty(ty) {
320 errors.push(err);
321 }
322 }
323}
324
325fn validate_trait_object_fn_ptr_ret_ty(ty: ast::FnPtrType, errors: &mut Vec<SyntaxError>) {
326 if let Some(ast::Type::DynTraitType(ty)) = ty.ret_type().and_then(|ty| ty.ty()) {
327 if let Some(err) = validate_trait_object_ty(ty) {
328 errors.push(err);
329 }
330 }
331}
332
333fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> {
334 let tbl = ty.type_bound_list()?;
335
336 if tbl.bounds().count() > 1 {
337 let dyn_token = ty.dyn_token()?;
338 let potential_parentheses =
339 algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?;
340 let kind = potential_parentheses.kind();
341 if !matches!(kind, T!['('] | T![<] | T![=]) {
342 return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()));
343 }
344 }
345 None
346}
diff --git a/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast
new file mode 100644
index 000000000..592741cdb
--- /dev/null
+++ b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast
@@ -0,0 +1,192 @@
1[email protected]
2 [email protected]
3 [email protected] "type"
4 [email protected] " "
5 [email protected]
6 [email protected] "Foo"
7 [email protected]
8 [email protected] "<"
9 [email protected]
10 [email protected] "\'a"
11 [email protected] ">"
12 [email protected] " "
13 [email protected] "="
14 [email protected] " "
15 [email protected]
16 [email protected] "&"
17 [email protected] "\'a"
18 [email protected] " "
19 [email protected]
20 [email protected] "dyn"
21 [email protected] " "
22 [email protected]
23 [email protected]
24 [email protected]
25 [email protected]
26 [email protected]
27 [email protected]
28 [email protected] "Send"
29 [email protected] " "
30 [email protected] "+"
31 [email protected] " "
32 [email protected]
33 [email protected]
34 [email protected]
35 [email protected]
36 [email protected]
37 [email protected] "Sync"
38 [email protected] ";"
39 [email protected] "\n"
40 [email protected]
41 [email protected] "type"
42 [email protected] " "
43 [email protected]
44 [email protected] "Foo"
45 [email protected] " "
46 [email protected] "="
47 [email protected] " "
48 [email protected]
49 [email protected] "*"
50 [email protected] "const"
51 [email protected] " "
52 [email protected]
53 [email protected] "dyn"
54 [email protected] " "
55 [email protected]
56 [email protected]
57 [email protected]
58 [email protected]
59 [email protected]
60 [email protected]
61 [email protected] "Send"
62 [email protected] " "
63 [email protected] "+"
64 [email protected] " "
65 [email protected]
66 [email protected]
67 [email protected]
68 [email protected]
69 [email protected]
70 [email protected] "Sync"
71 [email protected] ";"
72 [email protected] "\n"
73 [email protected]
74 [email protected] "type"
75 [email protected] " "
76 [email protected]
77 [email protected] "Foo"
78 [email protected] " "
79 [email protected] "="
80 [email protected] " "
81 [email protected]
82 [email protected] "fn"
83 [email protected]
84 [email protected] "("
85 [email protected] ")"
86 [email protected] " "
87 [email protected]
88 [email protected] "->"
89 [email protected] " "
90 [email protected]
91 [email protected] "dyn"
92 [email protected] " "
93 [email protected]
94 [email protected]
95 [email protected]
96 [email protected]
97 [email protected]
98 [email protected]
99 [email protected] "Send"
100 [email protected] " "
101 [email protected] "+"
102 [email protected] " "
103 [email protected]
104 [email protected] "\'static"
105 [email protected] ";"
106 [email protected] "\n"
107 [email protected]
108 [email protected] "fn"
109 [email protected] " "
110 [email protected]
111 [email protected] "main"
112 [email protected]
113 [email protected] "("
114 [email protected] ")"
115 [email protected] " "
116 [email protected]
117 [email protected] "{"
118 [email protected] "\n "
119 [email protected]
120 [email protected] "let"
121 [email protected] " "
122 [email protected]
123 [email protected]
124 [email protected] "b"
125 [email protected] " "
126 [email protected] "="
127 [email protected] " "
128 [email protected]
129 [email protected]
130 [email protected] "("
131 [email protected]
132 [email protected] "&"
133 [email protected]
134 [email protected]
135 [email protected]
136 [email protected]
137 [email protected] "a"
138 [email protected] ")"
139 [email protected] " "
140 [email protected] "as"
141 [email protected] " "
142 [email protected]
143 [email protected] "&"
144 [email protected]
145 [email protected] "dyn"
146 [email protected] " "
147 [email protected]
148 [email protected]
149 [email protected]
150 [email protected]
151 [email protected]
152 [email protected]
153 [email protected] "Add"
154 [email protected]
155 [email protected] "<"
156 [email protected]
157 [email protected]
158 [email protected]
159 [email protected]
160 [email protected]
161 [email protected] "Other"
162 [email protected] ","
163 [email protected] " "
164 [email protected]
165 [email protected]
166 [email protected] "Output"
167 [email protected] " "
168 [email protected] "="
169 [email protected] " "
170 [email protected]
171 [email protected]
172 [email protected]
173 [email protected]
174 [email protected] "Addable"
175 [email protected] ">"
176 [email protected] " "
177 [email protected] "+"
178 [email protected] " "
179 [email protected]
180 [email protected]
181 [email protected]
182 [email protected]
183 [email protected]
184 [email protected] "Other"
185 [email protected] ";"
186 [email protected] "\n"
187 [email protected] "}"
188 [email protected] "\n"
189error 19..34: ambiguous `+` in a type
190error 54..69: ambiguous `+` in a type
191error 90..108: ambiguous `+` in a type
192error 143..183: ambiguous `+` in a type
diff --git a/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rs b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rs
new file mode 100644
index 000000000..3a73d81bb
--- /dev/null
+++ b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rs
@@ -0,0 +1,6 @@
1type Foo<'a> = &'a dyn Send + Sync;
2type Foo = *const dyn Send + Sync;
3type Foo = fn() -> dyn Send + 'static;
4fn main() {
5 let b = (&a) as &dyn Add<Other, Output = Addable> + Other;
6}
diff --git a/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast
new file mode 100644
index 000000000..0cd868a83
--- /dev/null
+++ b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast
@@ -0,0 +1,200 @@
1[email protected]
2 [email protected]
3 [email protected] "type"
4 [email protected] " "
5 [email protected]
6 [email protected] "Foo"
7 [email protected]
8 [email protected] "<"
9 [email protected]
10 [email protected] "\'a"
11 [email protected] ">"
12 [email protected] " "
13 [email protected] "="
14 [email protected] " "
15 [email protected]
16 [email protected] "&"
17 [email protected] "\'a"
18 [email protected] " "
19 [email protected]
20 [email protected] "("
21 [email protected]
22 [email protected] "dyn"
23 [email protected] " "
24 [email protected]
25 [email protected]
26 [email protected]
27 [email protected]
28 [email protected]
29 [email protected]
30 [email protected] "Send"
31 [email protected] " "
32 [email protected] "+"
33 [email protected] " "
34 [email protected]
35 [email protected]
36 [email protected]
37 [email protected]
38 [email protected]
39 [email protected] "Sync"
40 [email protected] ")"
41 [email protected] ";"
42 [email protected] "\n"
43 [email protected]
44 [email protected] "type"
45 [email protected] " "
46 [email protected]
47 [email protected] "Foo"
48 [email protected] " "
49 [email protected] "="
50 [email protected] " "
51 [email protected]
52 [email protected] "*"
53 [email protected] "const"
54 [email protected] " "
55 [email protected]
56 [email protected] "("
57 [email protected]
58 [email protected] "dyn"
59 [email protected] " "
60 [email protected]
61 [email protected]
62 [email protected]
63 [email protected]
64 [email protected]
65 [email protected]
66 [email protected] "Send"
67 [email protected] " "
68 [email protected] "+"
69 [email protected] " "
70 [email protected]
71 [email protected]
72 [email protected]
73 [email protected]
74 [email protected]
75 [email protected] "Sync"
76 [email protected] ")"
77 [email protected] ";"
78 [email protected] "\n"
79 [email protected]
80 [email protected] "type"
81 [email protected] " "
82 [email protected]
83 [email protected] "Foo"
84 [email protected] " "
85 [email protected] "="
86 [email protected] " "
87 [email protected]
88 [email protected] "fn"
89 [email protected]
90 [email protected] "("
91 [email protected] ")"
92 [email protected] " "
93 [email protected]
94 [email protected] "->"
95 [email protected] " "
96 [email protected]
97 [email protected] "("
98 [email protected]
99 [email protected] "dyn"
100 [email protected] " "
101 [email protected]
102 [email protected]
103 [email protected]
104 [email protected]
105 [email protected]
106 [email protected]
107 [email protected] "Send"
108 [email protected] " "
109 [email protected] "+"
110 [email protected] " "
111 [email protected]
112 [email protected] "\'static"
113 [email protected] ")"
114 [email protected] ";"
115 [email protected] "\n"
116 [email protected]
117 [email protected] "fn"
118 [email protected] " "
119 [email protected]
120 [email protected] "main"
121 [email protected]
122 [email protected] "("
123 [email protected] ")"
124 [email protected] " "
125 [email protected]
126 [email protected] "{"
127 [email protected] "\n "
128 [email protected]
129 [email protected] "let"
130 [email protected] " "
131 [email protected]
132 [email protected]
133 [email protected] "b"
134 [email protected] " "
135 [email protected] "="
136 [email protected] " "
137 [email protected]
138 [email protected]
139 [email protected] "("
140 [email protected]
141 [email protected] "&"
142 [email protected]
143 [email protected]
144 [email protected]
145 [email protected]
146 [email protected] "a"
147 [email protected] ")"
148 [email protected] " "
149 [email protected] "as"
150 [email protected] " "
151 [email protected]
152 [email protected] "&"
153 [email protected]
154 [email protected] "("
155 [email protected]
156 [email protected] "dyn"
157 [email protected] " "
158 [email protected]
159 [email protected]
160 [email protected]
161 [email protected]
162 [email protected]
163 [email protected]
164 [email protected] "Add"
165 [email protected]
166 [email protected] "<"
167 [email protected]
168 [email protected]
169 [email protected]
170 [email protected]
171 [email protected]
172 [email protected] "Other"
173 [email protected] ","
174 [email protected] " "
175 [email protected]
176 [email protected]
177 [email protected] "Output"
178 [email protected] " "
179 [email protected] "="
180 [email protected] " "
181 [email protected]
182 [email protected]
183 [email protected]
184 [email protected]
185 [email protected] "Addable"
186 [email protected] ">"
187 [email protected] " "
188 [email protected] "+"
189 [email protected] " "
190 [email protected]
191 [email protected]
192 [email protected]
193 [email protected]
194 [email protected]
195 [email protected] "Other"
196 [email protected] ")"
197 [email protected] ";"
198 [email protected] "\n"
199 [email protected] "}"
200 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rs b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rs
new file mode 100644
index 000000000..97eb79c48
--- /dev/null
+++ b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rs
@@ -0,0 +1,6 @@
1type Foo<'a> = &'a (dyn Send + Sync);
2type Foo = *const (dyn Send + Sync);
3type Foo = fn() -> (dyn Send + 'static);
4fn main() {
5 let b = (&a) as &(dyn Add<Other, Output = Addable> + Other);
6}
diff --git a/docs/dev/style.md b/docs/dev/style.md
index fb407afcd..59067d234 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -6,7 +6,9 @@ Our approach to "clean code" is two-fold:
6It is explicitly OK for a reviewer to flag only some nits in the PR, and then send a follow-up cleanup PR for things which are easier to explain by example, cc-ing the original author. 6It is explicitly OK for a reviewer to flag only some nits in the PR, and then send a follow-up cleanup PR for things which are easier to explain by example, cc-ing the original author.
7Sending small cleanup PRs (like renaming a single local variable) is encouraged. 7Sending small cleanup PRs (like renaming a single local variable) is encouraged.
8 8
9# Scale of Changes 9# General
10
11## Scale of Changes
10 12
11Everyone knows that it's better to send small & focused pull requests. 13Everyone knows that it's better to send small & focused pull requests.
12The problem is, sometimes you *have* to, eg, rewrite the whole compiler, and that just doesn't fit into a set of isolated PRs. 14The problem is, sometimes you *have* to, eg, rewrite the whole compiler, and that just doesn't fit into a set of isolated PRs.
@@ -45,13 +47,35 @@ That said, adding an innocent-looking `pub use` is a very simple way to break en
45Note: if you enjoyed this abstract hand-waving about boundaries, you might appreciate 47Note: if you enjoyed this abstract hand-waving about boundaries, you might appreciate
46https://www.tedinski.com/2018/02/06/system-boundaries.html 48https://www.tedinski.com/2018/02/06/system-boundaries.html
47 49
48# Crates.io Dependencies 50## Crates.io Dependencies
49 51
50We try to be very conservative with usage of crates.io dependencies. 52We try to be very conservative with usage of crates.io dependencies.
51Don't use small "helper" crates (exception: `itertools` is allowed). 53Don't use small "helper" crates (exception: `itertools` is allowed).
52If there's some general reusable bit of code you need, consider adding it to the `stdx` crate. 54If there's some general reusable bit of code you need, consider adding it to the `stdx` crate.
53 55
54# Minimal Tests 56## Commit Style
57
58We don't have specific rules around git history hygiene.
59Maintaining clean git history is strongly encouraged, but not enforced.
60Use rebase workflow, it's OK to rewrite history during PR review process.
61After you are happy with the state of the code, please use [interactive rebase](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) to squash fixup commits.
62
63Avoid @mentioning people in commit messages and pull request descriptions(they are added to commit message by bors).
64Such messages create a lot of duplicate notification traffic during rebases.
65
66## Clippy
67
68We don't enforce Clippy.
69A number of default lints have high false positive rate.
70Selectively patching false-positives with `allow(clippy)` is considered worse than not using Clippy at all.
71There's `cargo xtask lint` command which runs a subset of low-FPR lints.
72Careful tweaking of `xtask lint` is welcome.
73See also [rust-lang/clippy#5537](https://github.com/rust-lang/rust-clippy/issues/5537).
74Of course, applying Clippy suggestions is welcome as long as they indeed improve the code.
75
76# Code
77
78## Minimal Tests
55 79
56Most tests in rust-analyzer start with a snippet of Rust code. 80Most tests in rust-analyzer start with a snippet of Rust code.
57This snippets should be minimal -- if you copy-paste a snippet of real code into the tests, make sure to remove everything which could be removed. 81This snippets should be minimal -- if you copy-paste a snippet of real code into the tests, make sure to remove everything which could be removed.
@@ -65,119 +89,7 @@ There are many benefits to this:
65It also makes sense to format snippets more compactly (for example, by placing enum definitions like `enum E { Foo, Bar }` on a single line), 89It also makes sense to format snippets more compactly (for example, by placing enum definitions like `enum E { Foo, Bar }` on a single line),
66as long as they are still readable. 90as long as they are still readable.
67 91
68# Order of Imports 92## Preconditions
69
70Separate import groups with blank lines.
71Use one `use` per crate.
72
73```rust
74mod x;
75mod y;
76
77// First std.
78use std::{ ... }
79
80// Second, external crates (both crates.io crates and other rust-analyzer crates).
81use crate_foo::{ ... }
82use crate_bar::{ ... }
83
84// Then current crate.
85use crate::{}
86
87// Finally, parent and child modules, but prefer `use crate::`.
88use super::{}
89```
90
91Module declarations come before the imports.
92Order them in "suggested reading order" for a person new to the code base.
93
94# Import Style
95
96Qualify items from `hir` and `ast`.
97
98```rust
99// Good
100use syntax::ast;
101
102fn frobnicate(func: hir::Function, strukt: ast::StructDef) {}
103
104// Not as good
105use hir::Function;
106use syntax::ast::StructDef;
107
108fn frobnicate(func: Function, strukt: StructDef) {}
109```
110
111Avoid local `use MyEnum::*` imports.
112
113Prefer `use crate::foo::bar` to `use super::bar`.
114
115When implementing `Debug` or `Display`, import `std::fmt`:
116
117```rust
118// Good
119use std::fmt;
120
121impl fmt::Display for RenameError {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { .. }
123}
124
125// Not as good
126impl std::fmt::Display for RenameError {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { .. }
128}
129```
130
131# Order of Items
132
133Optimize for the reader who sees the file for the first time, and wants to get a general idea about what's going on.
134People read things from top to bottom, so place most important things first.
135
136Specifically, if all items except one are private, always put the non-private item on top.
137
138Put `struct`s and `enum`s first, functions and impls last.
139
140Do
141
142```rust
143// Good
144struct Foo {
145 bars: Vec<Bar>
146}
147
148struct Bar;
149```
150
151rather than
152
153```rust
154// Not as good
155struct Bar;
156
157struct Foo {
158 bars: Vec<Bar>
159}
160```
161
162# Variable Naming
163
164Use boring and long names for local variables ([yay code completion](https://github.com/rust-analyzer/rust-analyzer/pull/4162#discussion_r417130973)).
165The default name is a lowercased name of the type: `global_state: GlobalState`.
166Avoid ad-hoc acronyms and contractions, but use the ones that exist consistently (`db`, `ctx`, `acc`).
167
168Default names:
169
170* `res` -- "result of the function" local variable
171* `it` -- I don't really care about the name
172* `n_foo` -- number of foos
173* `foo_idx` -- index of `foo`
174
175# Collection types
176
177Prefer `rustc_hash::FxHashMap` and `rustc_hash::FxHashSet` instead of the ones in `std::collections`.
178They use a hasher that's slightly faster and using them consistently will reduce code size by some small amount.
179
180# Preconditions
181 93
182Express function preconditions in types and force the caller to provide them (rather than checking in callee): 94Express function preconditions in types and force the caller to provide them (rather than checking in callee):
183 95
@@ -199,9 +111,15 @@ fn frobnicate(walrus: Option<Walrus>) {
199 111
200Avoid preconditions that span across function boundaries: 112Avoid preconditions that span across function boundaries:
201 113
202
203```rust 114```rust
204// Good 115// Good
116fn main() {
117 let s: &str = ...;
118 if let Some(contents) = string_literal_contents(s) {
119
120 }
121}
122
205fn string_literal_contents(s: &str) -> Option<&str> { 123fn string_literal_contents(s: &str) -> Option<&str> {
206 if s.starts_with('"') && s.ends_with('"') { 124 if s.starts_with('"') && s.ends_with('"') {
207 Some(&s[1..s.len() - 1]) 125 Some(&s[1..s.len() - 1])
@@ -210,54 +128,37 @@ fn string_literal_contents(s: &str) -> Option<&str> {
210 } 128 }
211} 129}
212 130
213fn foo() { 131// Not as good
132fn main() {
214 let s: &str = ...; 133 let s: &str = ...;
215 if let Some(contents) = string_literal_contents(s) { 134 if is_string_literal(s) {
216 135 let contents = &s[1..s.len() - 1];
217 } 136 }
218} 137}
219 138
220// Not as good
221fn is_string_literal(s: &str) -> bool { 139fn is_string_literal(s: &str) -> bool {
222 s.starts_with('"') && s.ends_with('"') 140 s.starts_with('"') && s.ends_with('"')
223} 141}
224
225fn foo() {
226 let s: &str = ...;
227 if is_string_literal(s) {
228 let contents = &s[1..s.len() - 1];
229 }
230}
231``` 142```
232 143
233In the "Not as good" version, the precondition that `1` is a valid char boundary is checked in `is_string_literal` and used in `foo`. 144In the "Not as good" version, the precondition that `1` is a valid char boundary is checked in `is_string_literal` and used in `foo`.
234In the "Good" version, the precondition check and usage are checked in the same block, and then encoded in the types. 145In the "Good" version, the precondition check and usage are checked in the same block, and then encoded in the types.
235 146
236# Early Returns 147When checking a boolean precondition, prefer `if !invariant` to `if negated_invariant`:
237
238Do use early returns
239 148
240```rust 149```rust
241// Good 150// Good
242fn foo() -> Option<Bar> { 151if !(idx < len) {
243 if !condition() { 152 return None;
244 return None;
245 }
246
247 Some(...)
248} 153}
249 154
250// Not as good 155// Not as good
251fn foo() -> Option<Bar> { 156if idx >= len {
252 if condition() { 157 return None;
253 Some(...)
254 } else {
255 None
256 }
257} 158}
258``` 159```
259 160
260# Getters & Setters 161## Getters & Setters
261 162
262If a field can have any value without breaking invariants, make the field public. 163If a field can have any value without breaking invariants, make the field public.
263Conversely, if there is an invariant, document it, enforce it in the "constructor" function, make the field private, and provide a getter. 164Conversely, if there is an invariant, document it, enforce it in the "constructor" function, make the field private, and provide a getter.
@@ -285,6 +186,40 @@ impl Person {
285} 186}
286``` 187```
287 188
189## Avoid Monomorphization
190
191Rust uses monomorphization to compile generic code, meaning that for each instantiation of a generic functions with concrete types, the function is compiled afresh, *per crate*.
192This allows for exceptionally good performance, but leads to increased compile times.
193Runtime performance obeys 80%/20% rule -- only a small fraction of code is hot.
194Compile time **does not** obey this rule -- all code has to be compiled.
195For this reason, avoid making a lot of code type parametric, *especially* on the boundaries between crates.
196
197```rust
198// Good
199fn frbonicate(f: impl FnMut()) {
200 frobnicate_impl(&mut f)
201}
202fn frobnicate_impl(f: &mut dyn FnMut()) {
203 // lots of code
204}
205
206// Not as good
207fn frbonicate(f: impl FnMut()) {
208 // lots of code
209}
210```
211
212Avoid `AsRef` polymorphism, it pays back only for widely used libraries:
213
214```rust
215// Good
216fn frbonicate(f: &Path) {
217}
218
219// Not as good
220fn frbonicate(f: impl AsRef<Path>) {
221}
222```
288 223
289# Premature Pessimization 224# Premature Pessimization
290 225
@@ -322,62 +257,159 @@ fn frobnicate(s: &str) {
322} 257}
323``` 258```
324 259
325# Avoid Monomorphization 260## Collection types
326 261
327Rust uses monomorphization to compile generic code, meaning that for each instantiation of a generic functions with concrete types, the function is compiled afresh, *per crate*. 262Prefer `rustc_hash::FxHashMap` and `rustc_hash::FxHashSet` instead of the ones in `std::collections`.
328This allows for exceptionally good performance, but leads to increased compile times. 263They use a hasher that's slightly faster and using them consistently will reduce code size by some small amount.
329Runtime performance obeys 80%/20% rule -- only a small fraction of code is hot. 264
330Compile time **does not** obey this rule -- all code has to be compiled. 265# Style
331For this reason, avoid making a lot of code type parametric, *especially* on the boundaries between crates. 266
267## Order of Imports
268
269Separate import groups with blank lines.
270Use one `use` per crate.
271
272```rust
273mod x;
274mod y;
275
276// First std.
277use std::{ ... }
278
279// Second, external crates (both crates.io crates and other rust-analyzer crates).
280use crate_foo::{ ... }
281use crate_bar::{ ... }
282
283// Then current crate.
284use crate::{}
285
286// Finally, parent and child modules, but prefer `use crate::`.
287use super::{}
288```
289
290Module declarations come before the imports.
291Order them in "suggested reading order" for a person new to the code base.
292
293## Import Style
294
295Qualify items from `hir` and `ast`.
332 296
333```rust 297```rust
334// Good 298// Good
335fn frbonicate(f: impl FnMut()) { 299use syntax::ast;
336 frobnicate_impl(&mut f) 300
337} 301fn frobnicate(func: hir::Function, strukt: ast::StructDef) {}
338fn frobnicate_impl(f: &mut dyn FnMut()) { 302
339 // lots of code 303// Not as good
304use hir::Function;
305use syntax::ast::StructDef;
306
307fn frobnicate(func: Function, strukt: StructDef) {}
308```
309
310Avoid local `use MyEnum::*` imports.
311
312Prefer `use crate::foo::bar` to `use super::bar`.
313
314When implementing `Debug` or `Display`, import `std::fmt`:
315
316```rust
317// Good
318use std::fmt;
319
320impl fmt::Display for RenameError {
321 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { .. }
340} 322}
341 323
342// Not as good 324// Not as good
343fn frbonicate(f: impl FnMut()) { 325impl std::fmt::Display for RenameError {
344 // lots of code 326 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { .. }
345} 327}
346``` 328```
347 329
348Avoid `AsRef` polymorphism, it pays back only for widely used libraries: 330## Order of Items
331
332Optimize for the reader who sees the file for the first time, and wants to get a general idea about what's going on.
333People read things from top to bottom, so place most important things first.
334
335Specifically, if all items except one are private, always put the non-private item on top.
336
337Put `struct`s and `enum`s first, functions and impls last.
338
339Do
349 340
350```rust 341```rust
351// Good 342// Good
352fn frbonicate(f: &Path) { 343struct Foo {
344 bars: Vec<Bar>
353} 345}
354 346
347struct Bar;
348```
349
350rather than
351
352```rust
355// Not as good 353// Not as good
356fn frbonicate(f: impl AsRef<Path>) { 354struct Bar;
355
356struct Foo {
357 bars: Vec<Bar>
357} 358}
358``` 359```
359 360
360# Documentation 361## Variable Naming
361 362
362For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. 363Use boring and long names for local variables ([yay code completion](https://github.com/rust-analyzer/rust-analyzer/pull/4162#discussion_r417130973)).
363If the line is too long, you want to split the sentence in two :-) 364The default name is a lowercased name of the type: `global_state: GlobalState`.
365Avoid ad-hoc acronyms and contractions, but use the ones that exist consistently (`db`, `ctx`, `acc`).
364 366
365# Commit Style 367Default names:
366 368
367We don't have specific rules around git history hygiene. 369* `res` -- "result of the function" local variable
368Maintaining clean git history is strongly encouraged, but not enforced. 370* `it` -- I don't really care about the name
369Use rebase workflow, it's OK to rewrite history during PR review process. 371* `n_foo` -- number of foos
370After you are happy with the state of the code, please use [interactive rebase](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) to squash fixup commits. 372* `foo_idx` -- index of `foo`
371 373
372Avoid @mentioning people in commit messages and pull request descriptions(they are added to commit message by bors).
373Such messages create a lot of duplicate notification traffic during rebases.
374 374
375# Clippy 375## Early Returns
376 376
377We don't enforce Clippy. 377Do use early returns
378A number of default lints have high false positive rate. 378
379Selectively patching false-positives with `allow(clippy)` is considered worse than not using Clippy at all. 379```rust
380There's `cargo xtask lint` command which runs a subset of low-FPR lints. 380// Good
381Careful tweaking of `xtask lint` is welcome. 381fn foo() -> Option<Bar> {
382See also [rust-lang/clippy#5537](https://github.com/rust-lang/rust-clippy/issues/5537). 382 if !condition() {
383Of course, applying Clippy suggestions is welcome as long as they indeed improve the code. 383 return None;
384 }
385
386 Some(...)
387}
388
389// Not as good
390fn foo() -> Option<Bar> {
391 if condition() {
392 Some(...)
393 } else {
394 None
395 }
396}
397```
398
399## Comparisons
400
401Use `<`/`<=`, avoid `>`/`>=`.
402Less-then comparisons are more intuitive, they correspond spatially to [real line](https://en.wikipedia.org/wiki/Real_line)
403
404```rust
405// Good
406assert!(lo <= x && x <= hi);
407
408// Not as good
409assert!(x >= lo && x <= hi>);
410```
411
412## Documentation
413
414For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines.
415If the line is too long, you want to split the sentence in two :-)
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index c1a778852..46e7bd091 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -397,6 +397,23 @@ It is possible to change the foreground/background color of inlay hints. Just ad
397} 397}
398---- 398----
399 399
400==== Semantic style customizations
401
402You can customize the look of different semantic elements in the source code. For example, mutable bindings are underlined by default and you can override this behavior by adding the following section to your `settings.json`:
403
404[source,jsonc]
405----
406{
407 "editor.semanticTokenColorCustomizations": {
408 "rules": {
409 "*.mutable": {
410 "fontStyle": "", // underline is the default
411 },
412 }
413 },
414}
415----
416
400==== Special `when` clause context for keybindings. 417==== Special `when` clause context for keybindings.
401You may use `inRustProject` context to configure keybindings for rust projects only. For example: 418You may use `inRustProject` context to configure keybindings for rust projects only. For example:
402[source,json] 419[source,json]