1use std::{borrow::Cow, env, mem, ops::Not};
3
4use either::Either;
5use hir::{
6 Adt, AsAssocItem, AsExternAssocItem, CaptureKind, DisplayTarget, DropGlue,
7 DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError,
8 MethodViolationCode, Name, Semantics, Symbol, Trait, Type, TypeInfo, Variant,
9};
10use ide_db::{
11 RootDatabase,
12 defs::{Definition, find_std_module},
13 documentation::{Documentation, HasDocs},
14 famous_defs::FamousDefs,
15 generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
16 syntax_helpers::prettify_macro_expansion,
17};
18use itertools::Itertools;
19use rustc_apfloat::{
20 Float,
21 ieee::{Half as f16, Quad as f128},
22};
23use span::{Edition, TextSize};
24use stdx::format_to;
25use syntax::{AstNode, AstToken, Direction, SyntaxToken, T, algo, ast, match_ast};
26
27use crate::{
28 HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig,
29 MemoryLayoutHoverRenderKind,
30 doc_links::{remove_links, rewrite_links},
31 hover::{SubstTyLen, notable_traits, walk_and_push_ty},
32 interpret::render_const_eval_error,
33};
34
35pub(super) fn type_info_of(
36 sema: &Semantics<'_, RootDatabase>,
37 _config: &HoverConfig<'_>,
38 expr_or_pat: &Either<ast::Expr, ast::Pat>,
39 edition: Edition,
40 display_target: DisplayTarget,
41) -> Option<HoverResult> {
42 let ty_info = match expr_or_pat {
43 Either::Left(expr) => sema.type_of_expr(expr)?,
44 Either::Right(pat) => sema.type_of_pat(pat)?,
45 };
46 type_info(sema, _config, ty_info, edition, display_target)
47}
48
49pub(super) fn closure_expr(
50 sema: &Semantics<'_, RootDatabase>,
51 config: &HoverConfig<'_>,
52 c: ast::ClosureExpr,
53 edition: Edition,
54 display_target: DisplayTarget,
55) -> Option<HoverResult> {
56 let TypeInfo { original, .. } = sema.type_of_expr(&c.into())?;
57 closure_ty(sema, config, &TypeInfo { original, adjusted: None }, edition, display_target)
58}
59
60pub(super) fn try_expr(
61 sema: &Semantics<'_, RootDatabase>,
62 _config: &HoverConfig<'_>,
63 try_expr: &ast::TryExpr,
64 edition: Edition,
65 display_target: DisplayTarget,
66) -> Option<HoverResult> {
67 let inner_ty = sema.type_of_expr(&try_expr.expr()?)?.original;
68 let mut ancestors = try_expr.syntax().ancestors();
69 let mut body_ty = loop {
70 let next = ancestors.next()?;
71 break match_ast! {
72 match next {
73 ast::Fn(fn_) => sema.to_def(&fn_)?.ret_type(sema.db),
74 ast::Item(__) => return None,
75 ast::ClosureExpr(closure) => sema.type_of_expr(&closure.body()?)?.original,
76 ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try { .. } | ast::BlockModifier::Const(_))) {
77 sema.type_of_expr(&block_expr.into())?.original
78 } else {
79 continue;
80 },
81 _ => continue,
82 }
83 };
84 };
85
86 if inner_ty == body_ty {
87 return None;
88 }
89
90 let mut inner_ty = inner_ty;
91 let mut s = "Try Target".to_owned();
92
93 let adts = inner_ty.as_adt().zip(body_ty.as_adt());
94 if let Some((hir::Adt::Enum(inner), hir::Adt::Enum(body))) = adts {
95 let famous_defs = FamousDefs(sema, sema.scope(try_expr.syntax())?.krate());
96 if let Some(option_enum) = famous_defs.core_option_Option()
98 && inner == option_enum
99 && body == option_enum
100 {
101 cov_mark::hit!(hover_try_expr_opt_opt);
102 return None;
103 }
104
105 if let Some(result_enum) = famous_defs.core_result_Result()
107 && inner == result_enum
108 && body == result_enum
109 {
110 let error_type_args =
111 inner_ty.type_arguments().nth(1).zip(body_ty.type_arguments().nth(1));
112 if let Some((inner, body)) = error_type_args {
113 inner_ty = inner;
114 body_ty = body;
115 "Try Error".clone_into(&mut s);
116 }
117 }
118 }
119
120 let mut res = HoverResult::default();
121
122 let mut targets: Vec<hir::ModuleDef> = Vec::new();
123 let mut push_new_def = |item: hir::ModuleDef| {
124 if !targets.contains(&item) {
125 targets.push(item);
126 }
127 };
128 walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def);
129 walk_and_push_ty(sema.db, &body_ty, &mut push_new_def);
130 if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) {
131 res.actions.push(actions);
132 }
133
134 let inner_ty = inner_ty.display(sema.db, display_target).to_string();
135 let body_ty = body_ty.display(sema.db, display_target).to_string();
136 let ty_len_max = inner_ty.len().max(body_ty.len());
137
138 let l = "Propagated as: ".len() - " Type: ".len();
139 let static_text_len_diff = l as isize - s.len() as isize;
140 let tpad = static_text_len_diff.max(0) as usize;
141 let ppad = static_text_len_diff.min(0).unsigned_abs();
142
143 res.markup = format!(
144 "```text\n{} Type: {:>pad0$}\nPropagated as: {:>pad1$}\n```\n",
145 s,
146 inner_ty,
147 body_ty,
148 pad0 = ty_len_max + tpad,
149 pad1 = ty_len_max + ppad,
150 )
151 .into();
152 Some(res)
153}
154
155pub(super) fn deref_expr(
156 sema: &Semantics<'_, RootDatabase>,
157 _config: &HoverConfig<'_>,
158 deref_expr: &ast::PrefixExpr,
159 edition: Edition,
160 display_target: DisplayTarget,
161) -> Option<HoverResult> {
162 let inner_ty = sema.type_of_expr(&deref_expr.expr()?)?.original;
163 let TypeInfo { original, adjusted } =
164 sema.type_of_expr(&ast::Expr::from(deref_expr.clone()))?;
165
166 let mut res = HoverResult::default();
167 let mut targets: Vec<hir::ModuleDef> = Vec::new();
168 let mut push_new_def = |item: hir::ModuleDef| {
169 if !targets.contains(&item) {
170 targets.push(item);
171 }
172 };
173 walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def);
174 walk_and_push_ty(sema.db, &original, &mut push_new_def);
175
176 res.markup = if let Some(adjusted_ty) = adjusted {
177 walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
178 let original = original.display(sema.db, display_target).to_string();
179 let adjusted = adjusted_ty.display(sema.db, display_target).to_string();
180 let inner = inner_ty.display(sema.db, display_target).to_string();
181 let type_len = "To type: ".len();
182 let coerced_len = "Coerced to: ".len();
183 let deref_len = "Dereferenced from: ".len();
184 let max_len = (original.len() + type_len)
185 .max(adjusted.len() + coerced_len)
186 .max(inner.len() + deref_len);
187 format!(
188 "```text\nDereferenced from: {:>ipad$}\nTo type: {:>apad$}\nCoerced to: {:>opad$}\n```\n",
189 inner,
190 original,
191 adjusted,
192 ipad = max_len - deref_len,
193 apad = max_len - type_len,
194 opad = max_len - coerced_len,
195 )
196 .into()
197 } else {
198 let original = original.display(sema.db, display_target).to_string();
199 let inner = inner_ty.display(sema.db, display_target).to_string();
200 let type_len = "To type: ".len();
201 let deref_len = "Dereferenced from: ".len();
202 let max_len = (original.len() + type_len).max(inner.len() + deref_len);
203 format!(
204 "```text\nDereferenced from: {:>ipad$}\nTo type: {:>apad$}\n```\n",
205 inner,
206 original,
207 ipad = max_len - deref_len,
208 apad = max_len - type_len,
209 )
210 .into()
211 };
212 if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) {
213 res.actions.push(actions);
214 }
215
216 Some(res)
217}
218
219pub(super) fn underscore(
220 sema: &Semantics<'_, RootDatabase>,
221 config: &HoverConfig<'_>,
222 token: &SyntaxToken,
223 edition: Edition,
224 display_target: DisplayTarget,
225) -> Option<HoverResult> {
226 if token.kind() != T![_] {
227 return None;
228 }
229 let parent = token.parent()?;
230 match_ast! {
231 match parent {
232 ast::InferType(it) => type_info(sema, config, TypeInfo { original: sema.resolve_type(&ast::Type::InferType(it))?, adjusted: None}, edition, display_target),
233 ast::UnderscoreExpr(it) => type_info(sema, config, sema.type_of_expr(&ast::Expr::UnderscoreExpr(it))?, edition, display_target),
234 ast::WildcardPat(it) => type_info(sema, config, sema.type_of_pat(&ast::Pat::WildcardPat(it))?, edition, display_target),
235 _ => None,
236 }
237 }
238}
239
240pub(super) fn keyword(
241 sema: &Semantics<'_, RootDatabase>,
242 config: &HoverConfig<'_>,
243 token: &SyntaxToken,
244 edition: Edition,
245 display_target: DisplayTarget,
246) -> Option<HoverResult> {
247 if !token.kind().is_keyword(edition) || !config.documentation || !config.keywords {
248 return None;
249 }
250 let parent = token.parent()?;
251 let famous_defs = FamousDefs(sema, sema.scope(&parent)?.krate());
252
253 let KeywordHint { description, keyword_mod, actions } =
254 keyword_hints(sema, token, parent, edition, display_target);
255
256 let doc_owner = find_std_module(&famous_defs, &keyword_mod, edition)?;
257 let docs = doc_owner.docs_with_rangemap(sema.db)?;
258 let (markup, range_map) =
259 markup(Some(Either::Left(docs)), description, None, None, String::new());
260 let markup = process_markup(sema.db, Definition::Module(doc_owner), &markup, range_map, config);
261 Some(HoverResult { markup, actions })
262}
263
264pub(super) fn struct_rest_pat(
268 sema: &Semantics<'_, RootDatabase>,
269 _config: &HoverConfig<'_>,
270 pattern: &ast::RecordPat,
271 edition: Edition,
272 display_target: DisplayTarget,
273) -> HoverResult {
274 let matched_fields = sema.record_pattern_matched_fields(pattern);
275
276 let mut res = HoverResult::default();
281 let mut targets: Vec<hir::ModuleDef> = Vec::new();
282 let mut push_new_def = |item: hir::ModuleDef| {
283 if !targets.contains(&item) {
284 targets.push(item);
285 }
286 };
287 for (_, t) in &matched_fields {
288 walk_and_push_ty(sema.db, t, &mut push_new_def);
289 }
290
291 res.markup = {
292 let mut s = String::from(".., ");
293 for (f, _) in &matched_fields {
294 s += f.display(sema.db, display_target).to_string().as_ref();
295 s += ", ";
296 }
297 s.truncate(s.len() - 2);
299
300 Markup::fenced_block(&s)
301 };
302 if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) {
303 res.actions.push(actions);
304 }
305 res
306}
307
308pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<HoverResult> {
309 let (path, tt) = attr.as_simple_call()?;
310 if !tt.syntax().text_range().contains(token.text_range().start()) {
311 return None;
312 }
313 let (is_clippy, lints) = match &*path {
314 "feature" => (false, FEATURES),
315 "allow" | "deny" | "expect" | "forbid" | "warn" => {
316 let is_clippy = algo::non_trivia_sibling(token.clone().into(), Direction::Prev)
317 .filter(|t| t.kind() == T![:])
318 .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
319 .filter(|t| t.kind() == T![:])
320 .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
321 .is_some_and(|t| {
322 t.kind() == T![ident] && t.into_token().is_some_and(|t| t.text() == "clippy")
323 });
324 if is_clippy { (true, CLIPPY_LINTS) } else { (false, DEFAULT_LINTS) }
325 }
326 _ => return None,
327 };
328
329 let needle = if is_clippy { &format!("clippy::{}", token.text()) } else { token.text() };
330
331 let lint =
332 lints.binary_search_by_key(&needle, |lint| lint.label).ok().map(|idx| &lints[idx])?;
333 Some(HoverResult {
334 markup: Markup::from(format!("```\n{}\n```\n---\n\n{}", lint.label, lint.description)),
335 ..Default::default()
336 })
337}
338
339pub(super) fn process_markup(
340 db: &RootDatabase,
341 def: Definition<'_>,
342 markup: &Markup,
343 markup_range_map: Option<hir::Docs>,
344 config: &HoverConfig<'_>,
345) -> Markup {
346 let markup = markup.as_str();
347 let markup = if config.links_in_hover {
348 rewrite_links(db, markup, def, markup_range_map.as_ref())
349 } else {
350 remove_links(markup)
351 };
352 Markup::from(markup)
353}
354
355fn definition_owner_name(
356 db: &RootDatabase,
357 def: Definition<'_>,
358 edition: Edition,
359) -> Option<String> {
360 match def {
361 Definition::Field(f) => {
362 let parent = f.parent_def(db);
363 let parent_name = parent.name(db);
364 let parent_name = parent_name.display(db, edition).to_string();
365 return match parent {
366 Variant::EnumVariant(variant) => {
367 let enum_name = variant.parent_enum(db).name(db);
368 Some(format!("{}::{parent_name}", enum_name.display(db, edition)))
369 }
370 _ => Some(parent_name),
371 };
372 }
373 Definition::EnumVariant(e) => Some(e.parent_enum(db).name(db)),
374 Definition::GenericParam(generic_param) => match generic_param.parent() {
375 hir::GenericDef::Adt(it) => Some(it.name(db)),
376 hir::GenericDef::Trait(it) => Some(it.name(db)),
377 hir::GenericDef::TypeAlias(it) => Some(it.name(db)),
378
379 hir::GenericDef::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)),
380 hir::GenericDef::Function(it) => {
381 let container = it.as_assoc_item(db).and_then(|assoc| match assoc.container(db) {
382 hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
383 hir::AssocItemContainer::Impl(i) => {
384 i.self_ty(db).as_adt().map(|adt| adt.name(db))
385 }
386 });
387 match container {
388 Some(name) => {
389 return Some(format!(
390 "{}::{}",
391 name.display(db, edition),
392 it.name(db).display(db, edition)
393 ));
394 }
395 None => Some(it.name(db)),
396 }
397 }
398 hir::GenericDef::Const(it) => {
399 let container = it.as_assoc_item(db).and_then(|assoc| match assoc.container(db) {
400 hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
401 hir::AssocItemContainer::Impl(i) => {
402 i.self_ty(db).as_adt().map(|adt| adt.name(db))
403 }
404 });
405 match container {
406 Some(name) => {
407 return Some(format!(
408 "{}::{}",
409 name.display(db, edition),
410 it.name(db)?.display(db, edition)
411 ));
412 }
413 None => it.name(db),
414 }
415 }
416 hir::GenericDef::Static(it) => Some(it.name(db)),
417 },
418 Definition::DeriveHelper(derive_helper) => Some(derive_helper.derive().name(db)),
419 d => {
420 if let Some(assoc_item) = d.as_assoc_item(db) {
421 match assoc_item.container(db) {
422 hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
423 hir::AssocItemContainer::Impl(i) => {
424 i.self_ty(db).as_adt().map(|adt| adt.name(db))
425 }
426 }
427 } else {
428 return d.as_extern_assoc_item(db).map(|_| "<extern>".to_owned());
429 }
430 }
431 }
432 .map(|name| name.display(db, edition).to_string())
433}
434
435pub(super) fn path(
436 db: &RootDatabase,
437 module: hir::Module,
438 item_name: Option<String>,
439 edition: Edition,
440) -> String {
441 let crate_name = module.krate(db).display_name(db).as_ref().map(|it| it.to_string());
442 let module_path = module.path_segments(db).map(|it| it.display(db, edition).to_string());
443 crate_name.into_iter().chain(module_path).chain(item_name).join("::")
444}
445
446pub(super) fn definition(
447 db: &RootDatabase,
448 def: Definition<'_>,
449 famous_defs: Option<&FamousDefs<'_, '_>>,
450 notable_traits: &[(Trait, Vec<(Option<Type<'_>>, Name)>)],
451 macro_arm: Option<u32>,
452 render_extras: bool,
453 render_private_fields: bool,
454 subst_types: Option<&Vec<(Symbol, Type<'_>)>>,
455 config: &HoverConfig<'_>,
456 edition: Edition,
457 display_target: DisplayTarget,
458) -> (Markup, Option<hir::Docs>) {
459 let mod_path = definition_path(db, &def, edition);
460 let label = match def {
461 Definition::Trait(trait_) => trait_
462 .display_limited(db, config.max_trait_assoc_items_count, display_target)
463 .with_private_fields(render_private_fields)
464 .to_string(),
465 Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => adt
466 .display_limited(db, config.max_fields_count, display_target)
467 .with_private_fields(render_private_fields)
468 .to_string(),
469 Definition::EnumVariant(variant) => variant
470 .display_limited(db, config.max_fields_count, display_target)
471 .with_private_fields(render_private_fields)
472 .to_string(),
473 Definition::Adt(adt @ Adt::Enum(_)) => adt
474 .display_limited(db, config.max_enum_variants_count, display_target)
475 .with_private_fields(render_private_fields)
476 .to_string(),
477 Definition::SelfType(impl_def) => {
478 let self_ty = &impl_def.self_ty(db);
479 match self_ty.as_adt() {
480 Some(adt) => adt
481 .display_limited(db, config.max_fields_count, display_target)
482 .with_private_fields(render_private_fields)
483 .to_string(),
484 None => self_ty.display(db, display_target).to_string(),
485 }
486 }
487 Definition::Macro(it) => {
488 let mut label = it.display(db, display_target).to_string();
489 if let Some(macro_arm) = macro_arm {
490 format_to!(label, " // matched arm #{}", macro_arm);
491 }
492 label
493 }
494 Definition::Function(fn_) => {
495 fn_.display_with_container_bounds(db, true, display_target).to_string()
496 }
497 _ => def.label(db, display_target),
498 };
499 let docs = if config.documentation {
500 def.docs_with_rangemap(db, famous_defs, display_target)
501 } else {
502 None
503 };
504 let value = || match def {
505 Definition::EnumVariant(it) => {
506 if !it.parent_enum(db).is_data_carrying(db) {
507 match it.eval(db) {
508 Ok(it) => {
509 Some(if it >= 10 { format!("{it} ({it:#X})") } else { format!("{it}") })
510 }
511 Err(err) => {
512 let res = it.value(db).map(|it| format!("{it:?}"));
513 if env::var_os("RA_DEV").is_some() {
514 let res = res.as_deref().unwrap_or("");
515 Some(format!(
516 "{res} ({})",
517 render_const_eval_error(db, err, display_target)
518 ))
519 } else {
520 res
521 }
522 }
523 }
524 } else {
525 None
526 }
527 }
528 Definition::Const(it) => {
529 let body = it.eval(db);
530 Some(match body {
531 Ok(it) => match it.render_debug(db) {
532 Ok(rendered) if rendered.is_empty() => it.render(db, display_target),
533 Ok(rendered) => rendered,
534 Err(err) => {
535 let it = it.render(db, display_target);
536 if env::var_os("RA_DEV").is_some() {
537 format!(
538 "{it}\n{}",
539 render_const_eval_error(db, err.into(), display_target)
540 )
541 } else {
542 it
543 }
544 }
545 },
546 Err(err) => {
547 let source = it.source(db)?;
548 let mut body = source.value.body()?.syntax().clone();
549 if let Some(macro_file) = source.file_id.macro_file() {
550 let span_map = macro_file.expansion_span_map(db);
551 body = prettify_macro_expansion(db, body, span_map, it.krate(db).into());
552 }
553 if env::var_os("RA_DEV").is_some() {
554 format!("{body}\n{}", render_const_eval_error(db, err, display_target))
555 } else {
556 body.to_string()
557 }
558 }
559 })
560 }
561 Definition::Static(it) => {
562 let body = it.eval(db);
563 Some(match body {
564 Ok(it) => match it.render_debug(db) {
565 Ok(rendered) if rendered.is_empty() => it.render(db, display_target),
566 Ok(rendered) => rendered,
567
568 Err(err) => {
569 let it = it.render(db, display_target);
570 if env::var_os("RA_DEV").is_some() {
571 format!(
572 "{it}\n{}",
573 render_const_eval_error(db, err.into(), display_target)
574 )
575 } else {
576 it
577 }
578 }
579 },
580 Err(err) => {
581 let source = it.source(db)?;
582 let mut body = source.value.body()?.syntax().clone();
583 if let Some(macro_file) = source.file_id.macro_file() {
584 let span_map = macro_file.expansion_span_map(db);
585 body = prettify_macro_expansion(db, body, span_map, it.krate(db).into());
586 }
587 if env::var_os("RA_DEV").is_some() {
588 format!("{body}\n{}", render_const_eval_error(db, err, display_target))
589 } else {
590 body.to_string()
591 }
592 }
593 })
594 }
595 _ => None,
596 };
597
598 let layout_info = || match def {
599 Definition::Field(it) => render_memory_layout(
600 config.memory_layout,
601 || it.layout(db),
602 |_| {
603 let var_def = it.parent_def(db);
604 match var_def {
605 hir::Variant::Struct(s) => {
606 Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(it))
607 }
608 _ => None,
609 }
610 },
611 |_| None,
612 |_| None,
613 ),
614 Definition::Adt(it @ Adt::Struct(strukt)) => render_memory_layout(
615 config.memory_layout,
616 || it.layout(db),
617 |_| None,
618 |layout| {
619 let mut field_size =
620 |i: usize| Some(strukt.fields(db).get(i)?.layout(db).ok()?.size());
621 if strukt.repr(db).is_some_and(|it| it.inhibit_struct_field_reordering()) {
622 Some(("tail padding", layout.tail_padding(&mut field_size)?))
623 } else {
624 Some(("largest padding", layout.largest_padding(&mut field_size)?))
625 }
626 },
627 |_| None,
628 ),
629 Definition::Adt(it) => render_memory_layout(
630 config.memory_layout,
631 || it.layout(db),
632 |_| None,
633 |_| None,
634 |_| None,
635 ),
636 Definition::EnumVariant(it) => render_memory_layout(
637 config.memory_layout,
638 || it.layout(db),
639 |_| None,
640 |_| None,
641 |layout| layout.enum_tag_size(),
642 ),
643 Definition::TypeAlias(it) => render_memory_layout(
644 config.memory_layout,
645 || it.ty(db).layout(db),
646 |_| None,
647 |_| None,
648 |_| None,
649 ),
650 Definition::Local(it) => render_memory_layout(
651 config.memory_layout,
652 || it.ty(db).layout(db),
653 |_| None,
654 |_| None,
655 |_| None,
656 ),
657 Definition::SelfType(it) => render_memory_layout(
658 config.memory_layout,
659 || it.self_ty(db).layout(db),
660 |_| None,
661 |_| None,
662 |_| None,
663 ),
664 _ => None,
665 };
666
667 let drop_info = || {
668 if !config.show_drop_glue {
669 return None;
670 }
671 let drop_info = match def {
672 Definition::Field(field) => {
673 DropInfo { drop_glue: field.ty(db).drop_glue(db), has_dtor: None }
674 }
675 Definition::Adt(Adt::Struct(strukt)) => {
676 let struct_drop_glue = strukt.ty(db).drop_glue(db);
677 let mut fields_drop_glue = strukt
678 .fields(db)
679 .iter()
680 .map(|field| field.ty(db).drop_glue(db))
681 .max()
682 .unwrap_or(DropGlue::None);
683 let has_dtor = match (fields_drop_glue, struct_drop_glue) {
684 (DropGlue::None, _) => struct_drop_glue != DropGlue::None,
685 (_, DropGlue::None) => {
686 fields_drop_glue = DropGlue::None;
688 false
689 }
690 (_, _) => struct_drop_glue > fields_drop_glue,
691 };
692 DropInfo { drop_glue: fields_drop_glue, has_dtor: Some(has_dtor) }
693 }
694 Definition::Adt(Adt::Union(union)) => DropInfo {
696 drop_glue: DropGlue::None,
697 has_dtor: Some(union.ty(db).drop_glue(db) != DropGlue::None),
698 },
699 Definition::Adt(Adt::Enum(enum_)) => {
700 let enum_drop_glue = enum_.ty(db).drop_glue(db);
701 let fields_drop_glue = enum_
702 .variants(db)
703 .iter()
704 .map(|variant| {
705 variant
706 .fields(db)
707 .iter()
708 .map(|field| field.ty(db).drop_glue(db))
709 .max()
710 .unwrap_or(DropGlue::None)
711 })
712 .max()
713 .unwrap_or(DropGlue::None);
714 DropInfo {
715 drop_glue: fields_drop_glue,
716 has_dtor: Some(enum_drop_glue > fields_drop_glue),
717 }
718 }
719 Definition::EnumVariant(variant) => {
720 let fields_drop_glue = variant
721 .fields(db)
722 .iter()
723 .map(|field| field.ty(db).drop_glue(db))
724 .max()
725 .unwrap_or(DropGlue::None);
726 DropInfo { drop_glue: fields_drop_glue, has_dtor: None }
727 }
728 Definition::TypeAlias(type_alias) => {
729 DropInfo { drop_glue: type_alias.ty(db).drop_glue(db), has_dtor: None }
730 }
731 Definition::Local(local) => {
732 DropInfo { drop_glue: local.ty(db).drop_glue(db), has_dtor: None }
733 }
734 _ => return None,
735 };
736 let rendered_drop_glue = if drop_info.has_dtor == Some(true) {
737 "impl Drop"
738 } else {
739 match drop_info.drop_glue {
740 DropGlue::HasDropGlue => "needs Drop",
741 DropGlue::None => "no Drop",
742 DropGlue::DependOnParams => "type param may need Drop",
743 }
744 };
745
746 Some(rendered_drop_glue.to_owned())
747 };
748
749 let dyn_compatibility_info = || match def {
750 Definition::Trait(it) => {
751 let mut dyn_compatibility_info = String::new();
752 render_dyn_compatibility(db, &mut dyn_compatibility_info, it.dyn_compatibility(db));
753 Some(dyn_compatibility_info)
754 }
755 _ => None,
756 };
757
758 let variance_info = || match def {
759 Definition::GenericParam(it) => it.variance(db).as_ref().map(ToString::to_string),
760 _ => None,
761 };
762
763 let mut extra = String::new();
764 if render_extras {
765 if let Some(notable_traits) =
766 render_notable_trait(db, notable_traits, edition, display_target)
767 {
768 extra.push_str("\n___\n");
769 extra.push_str(¬able_traits);
770 }
771 if let Some(variance_info) = variance_info() {
772 extra.push_str("\n___\n");
773 extra.push_str(&variance_info);
774 }
775 if let Some(layout_info) = layout_info() {
776 extra.push_str("\n___\n");
777 extra.push_str(&layout_info);
778 if let Some(drop_info) = drop_info() {
779 extra.push_str(", ");
780 extra.push_str(&drop_info)
781 }
782 } else if let Some(drop_info) = drop_info() {
783 extra.push_str("\n___\n");
784 extra.push_str(&drop_info);
785 }
786 if let Some(dyn_compatibility_info) = dyn_compatibility_info() {
787 extra.push_str("\n___\n");
788 extra.push_str(&dyn_compatibility_info);
789 }
790 }
791 let mut desc = String::new();
792 desc.push_str(&label);
793 if let Some(value) = value() {
794 desc.push_str(" = ");
795 desc.push_str(&value);
796 }
797
798 let subst_types = match config.max_subst_ty_len {
799 SubstTyLen::Hide => String::new(),
800 SubstTyLen::LimitTo(_) | SubstTyLen::Unlimited => {
801 let limit = if let SubstTyLen::LimitTo(limit) = config.max_subst_ty_len {
802 Some(limit)
803 } else {
804 None
805 };
806 subst_types
807 .map(|subst_type| {
808 subst_type
809 .iter()
810 .filter(|(_, ty)| !ty.is_unknown())
811 .format_with(", ", |(name, ty), fmt| {
812 fmt(&format_args!(
813 "`{name}` = `{}`",
814 ty.display_truncated(db, limit, display_target)
815 ))
816 })
817 .to_string()
818 })
819 .unwrap_or_default()
820 }
821 };
822
823 markup(docs, desc, extra.is_empty().not().then_some(extra), mod_path, subst_types)
824}
825
826#[derive(Debug)]
827struct DropInfo {
828 drop_glue: DropGlue,
829 has_dtor: Option<bool>,
830}
831
832pub(super) fn literal(
833 sema: &Semantics<'_, RootDatabase>,
834 token: SyntaxToken,
835 display_target: DisplayTarget,
836) -> Option<Markup> {
837 let lit = token.parent().and_then(ast::Literal::cast)?;
838 let ty = if let Some(p) = lit.syntax().parent().and_then(ast::Pat::cast) {
839 sema.type_of_pat(&p)?
840 } else {
841 sema.type_of_expr(&ast::Expr::Literal(lit))?
842 }
843 .original;
844
845 let value = match_ast! {
846 match token {
847 ast::String(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(ToString::to_string),
848 ast::ByteString(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(|it| format!("{it:?}")),
849 ast::CString(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(|it| std::str::from_utf8(it).map_or_else(|e| format!("{e:?}"), ToOwned::to_owned)),
850 ast::Char(char) => char .value().as_ref().map_err(|e| format!("{e:?}")).map(ToString::to_string),
851 ast::Byte(byte) => byte .value().as_ref().map_err(|e| format!("{e:?}")).map(|it| format!("0x{it:X}")),
852 ast::FloatNumber(num) => {
853 let text = num.value_string();
854 if ty.as_builtin().map(|it| it.is_f16()).unwrap_or(false) {
855 match text.parse::<f16>() {
856 Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())),
857 Err(e) => Err(e.0.to_owned()),
858 }
859 } else if ty.as_builtin().map(|it| it.is_f32()).unwrap_or(false) {
860 match text.parse::<f32>() {
861 Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())),
862 Err(e) => Err(e.to_string()),
863 }
864 } else if ty.as_builtin().map(|it| it.is_f128()).unwrap_or(false) {
865 match text.parse::<f128>() {
866 Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())),
867 Err(e) => Err(e.0.to_owned()),
868 }
869 } else {
870 match text.parse::<f64>() {
871 Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())),
872 Err(e) => Err(e.to_string()),
873 }
874 }
875 },
876 ast::IntNumber(num) => match num.value() {
877 Ok(num) => Ok(format!("{num} (0x{num:X}|0b{num:b})")),
878 Err(e) => Err(e.to_string()),
879 },
880 _ => return None
881 }
882 };
883 let ty = ty.display(sema.db, display_target);
884
885 let mut s = format!("```rust\n{ty}\n```\n---\n\n");
886 match value {
887 Ok(value) => {
888 let backtick_len = value.chars().filter(|c| *c == '`').count();
889 let spaces_len = value.chars().filter(|c| *c == ' ').count();
890 let backticks = "`".repeat(backtick_len + 1);
891 let space_char = if spaces_len == value.len() { "" } else { " " };
892
893 if let Some(newline) = value.find('\n') {
894 format_to!(
895 s,
896 "value of literal (truncated up to newline): {backticks}{space_char}{}{space_char}{backticks}",
897 &value[..newline]
898 )
899 } else {
900 format_to!(
901 s,
902 "value of literal: {backticks}{space_char}{value}{space_char}{backticks}"
903 )
904 }
905 }
906 Err(error) => format_to!(s, "invalid literal: {error}"),
907 }
908 Some(s.into())
909}
910
911fn render_notable_trait(
912 db: &RootDatabase,
913 notable_traits: &[(Trait, Vec<(Option<Type<'_>>, Name)>)],
914 edition: Edition,
915 display_target: DisplayTarget,
916) -> Option<String> {
917 let mut desc = String::new();
918 let mut needs_impl_header = true;
919 for (trait_, assoc_types) in notable_traits {
920 desc.push_str(if mem::take(&mut needs_impl_header) {
921 "Implements notable traits: `"
922 } else {
923 "`, `"
924 });
925 format_to!(desc, "{}", trait_.name(db).display(db, edition));
926 if !assoc_types.is_empty() {
927 desc.push('<');
928 format_to!(
929 desc,
930 "{}",
931 assoc_types.iter().format_with(", ", |(ty, name), f| {
932 f(&name.display(db, edition))?;
933 f(&" = ")?;
934 match ty {
935 Some(ty) => f(&ty.display(db, display_target)),
936 None => f(&"?"),
937 }
938 })
939 );
940 desc.push('>');
941 }
942 }
943 if desc.is_empty() {
944 None
945 } else {
946 desc.push('`');
947 Some(desc)
948 }
949}
950
951fn type_info(
952 sema: &Semantics<'_, RootDatabase>,
953 config: &HoverConfig<'_>,
954 ty: TypeInfo<'_>,
955 edition: Edition,
956 display_target: DisplayTarget,
957) -> Option<HoverResult> {
958 if let Some(res) = closure_ty(sema, config, &ty, edition, display_target) {
959 return Some(res);
960 };
961 let db = sema.db;
962 let TypeInfo { original, adjusted } = ty;
963 let mut res = HoverResult::default();
964 let mut targets: Vec<hir::ModuleDef> = Vec::new();
965 let mut push_new_def = |item: hir::ModuleDef| {
966 if !targets.contains(&item) {
967 targets.push(item);
968 }
969 };
970 walk_and_push_ty(db, &original, &mut push_new_def);
971
972 res.markup = if let Some(adjusted_ty) = adjusted {
973 walk_and_push_ty(db, &adjusted_ty, &mut push_new_def);
974
975 let notable = if let Some(notable) =
976 render_notable_trait(db, ¬able_traits(db, &original), edition, display_target)
977 {
978 format!("{notable}\n")
979 } else {
980 String::new()
981 };
982
983 let original = original.display(db, display_target).to_string();
984 let adjusted = adjusted_ty.display(db, display_target).to_string();
985 let static_text_diff_len = "Coerced to: ".len() - "Type: ".len();
986 format!(
987 "```text\nType: {:>apad$}\nCoerced to: {:>opad$}\n{notable}```\n",
988 original,
989 adjusted,
990 apad = static_text_diff_len + adjusted.len().max(original.len()),
991 opad = original.len(),
992 )
993 .into()
994 } else {
995 let mut desc = format!("```rust\n{}\n```", original.display(db, display_target));
996 if let Some(extra) =
997 render_notable_trait(db, ¬able_traits(db, &original), edition, display_target)
998 {
999 desc.push_str("\n---\n");
1000 desc.push_str(&extra);
1001 };
1002 desc.into()
1003 };
1004 if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) {
1005 res.actions.push(actions);
1006 }
1007 Some(res)
1008}
1009
1010fn closure_ty(
1011 sema: &Semantics<'_, RootDatabase>,
1012 config: &HoverConfig<'_>,
1013 TypeInfo { original, adjusted }: &TypeInfo<'_>,
1014 edition: Edition,
1015 display_target: DisplayTarget,
1016) -> Option<HoverResult> {
1017 let c = original.as_closure()?;
1018 let captures = c.captured_items(sema.db);
1019 let mut captures_rendered = captures
1020 .iter()
1021 .map(|it| {
1022 let borrow_kind = match it.kind() {
1023 CaptureKind::SharedRef => "immutable borrow",
1024 CaptureKind::UniqueSharedRef => "unique immutable borrow ([read more](https://doc.rust-lang.org/stable/reference/types/closure.html#unique-immutable-borrows-in-captures))",
1025 CaptureKind::MutableRef => "mutable borrow",
1026 CaptureKind::Move => "move",
1027 };
1028 format!("* `{}` by {}", it.display_place_source_code(sema.db, display_target.edition), borrow_kind)
1029 })
1030 .join("\n");
1031 if captures_rendered.trim().is_empty() {
1032 "This closure captures nothing".clone_into(&mut captures_rendered);
1033 }
1034 let mut targets: Vec<hir::ModuleDef> = Vec::new();
1035 let mut push_new_def = |item: hir::ModuleDef| {
1036 if !targets.contains(&item) {
1037 targets.push(item);
1038 }
1039 };
1040 walk_and_push_ty(sema.db, original, &mut push_new_def);
1041 captures.iter().for_each(|capture| {
1042 walk_and_push_ty(sema.db, &capture.ty(sema.db), &mut push_new_def);
1043 });
1044
1045 let adjusted = if let Some(adjusted_ty) = adjusted {
1046 walk_and_push_ty(sema.db, adjusted_ty, &mut push_new_def);
1047 format!(
1048 "\nCoerced to: {}",
1049 adjusted_ty
1050 .display(sema.db, display_target)
1051 .with_closure_style(hir::ClosureStyle::ImplFn)
1052 )
1053 } else {
1054 String::new()
1055 };
1056 let mut markup = format!("```rust\n{}\n```", c.display_with_impl(sema.db, display_target));
1057
1058 if let Some(trait_) = c.fn_trait(sema.db).get_id(sema.db, original.krate(sema.db)) {
1059 push_new_def(trait_.into())
1060 }
1061 if let Some(layout) = render_memory_layout(
1062 config.memory_layout,
1063 || original.layout(sema.db),
1064 |_| None,
1065 |_| None,
1066 |_| None,
1067 ) {
1068 format_to!(markup, "\n---\n{layout}");
1069 }
1070 format_to!(markup, "{adjusted}\n\n## Captures\n{}", captures_rendered,);
1071
1072 let mut res = HoverResult::default();
1073 if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) {
1074 res.actions.push(actions);
1075 }
1076 res.markup = markup.into();
1077 Some(res)
1078}
1079
1080fn definition_path(db: &RootDatabase, &def: &Definition<'_>, edition: Edition) -> Option<String> {
1081 if matches!(
1082 def,
1083 Definition::TupleField(_)
1084 | Definition::Label(_)
1085 | Definition::Local(_)
1086 | Definition::BuiltinAttr(_)
1087 | Definition::BuiltinLifetime(_)
1088 | Definition::BuiltinType(_)
1089 | Definition::InlineAsmRegOrRegClass(_)
1090 | Definition::InlineAsmOperand(_)
1091 ) {
1092 return None;
1093 }
1094 let rendered_parent = definition_owner_name(db, def, edition);
1095 def.module(db).map(|module| path(db, module, rendered_parent, edition))
1096}
1097
1098fn markup(
1099 docs: Option<Either<Cow<'_, hir::Docs>, Documentation<'_>>>,
1100 rust: String,
1101 extra: Option<String>,
1102 mod_path: Option<String>,
1103 subst_types: String,
1104) -> (Markup, Option<hir::Docs>) {
1105 let mut buf = String::new();
1106
1107 if let Some(mod_path) = mod_path
1108 && !mod_path.is_empty()
1109 {
1110 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
1111 }
1112 format_to!(buf, "```rust\n{}\n```", rust);
1113
1114 if let Some(extra) = extra {
1115 buf.push_str(&extra);
1116 }
1117
1118 if !subst_types.is_empty() {
1119 format_to!(buf, "\n___\n{subst_types}");
1120 }
1121
1122 if let Some(doc) = docs {
1123 format_to!(buf, "\n___\n\n");
1124 let offset = TextSize::new(buf.len() as u32);
1125 let docs_str = match &doc {
1126 Either::Left(docs) => docs.docs(),
1127 Either::Right(docs) => docs.as_str(),
1128 };
1129 format_to!(buf, "{}", docs_str);
1130 let range_map = match doc {
1131 Either::Left(range_map) => {
1132 let mut range_map = range_map.into_owned();
1133 range_map.shift_by(offset);
1134 Some(range_map)
1135 }
1136 Either::Right(_) => None,
1137 };
1138
1139 (buf.into(), range_map)
1140 } else {
1141 (buf.into(), None)
1142 }
1143}
1144
1145fn render_memory_layout<'db>(
1146 config: Option<MemoryLayoutHoverConfig>,
1147 layout: impl FnOnce() -> Result<Layout<'db>, LayoutError>,
1148 offset: impl FnOnce(&Layout<'db>) -> Option<u64>,
1149 padding: impl for<'a> FnOnce(&'a Layout<'db>) -> Option<(&'a str, u64)>,
1150 tag: impl FnOnce(&Layout<'db>) -> Option<usize>,
1151) -> Option<String> {
1152 let config = config?;
1153 let layout = layout().ok()?;
1154
1155 let mut label = String::new();
1156
1157 if let Some(render) = config.size {
1158 let size = match tag(&layout) {
1159 Some(tag) => layout.size() as usize - tag,
1160 None => layout.size() as usize,
1161 };
1162 format_to!(label, "size = ");
1163 match render {
1164 MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{size}"),
1165 MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{size:#X}"),
1166 MemoryLayoutHoverRenderKind::Both if size >= 10 => {
1167 format_to!(label, "{size} ({size:#X})")
1168 }
1169 MemoryLayoutHoverRenderKind::Both => format_to!(label, "{size}"),
1170 }
1171 format_to!(label, ", ");
1172 }
1173
1174 if let Some(render) = config.alignment {
1175 let align = layout.align();
1176 format_to!(label, "align = ");
1177 match render {
1178 MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{align}",),
1179 MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{align:#X}",),
1180 MemoryLayoutHoverRenderKind::Both if align >= 10 => {
1181 format_to!(label, "{align} ({align:#X})")
1182 }
1183 MemoryLayoutHoverRenderKind::Both => {
1184 format_to!(label, "{align}")
1185 }
1186 }
1187 format_to!(label, ", ");
1188 }
1189
1190 if let Some(render) = config.offset
1191 && let Some(offset) = offset(&layout)
1192 {
1193 format_to!(label, "offset = ");
1194 match render {
1195 MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{offset}"),
1196 MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{offset:#X}"),
1197 MemoryLayoutHoverRenderKind::Both if offset >= 10 => {
1198 format_to!(label, "{offset} ({offset:#X})")
1199 }
1200 MemoryLayoutHoverRenderKind::Both => {
1201 format_to!(label, "{offset}")
1202 }
1203 }
1204 format_to!(label, ", ");
1205 }
1206
1207 if let Some(render) = config.padding
1208 && let Some((padding_name, padding)) = padding(&layout)
1209 {
1210 format_to!(label, "{padding_name} = ");
1211 match render {
1212 MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{padding}"),
1213 MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{padding:#X}"),
1214 MemoryLayoutHoverRenderKind::Both if padding >= 10 => {
1215 format_to!(label, "{padding} ({padding:#X})")
1216 }
1217 MemoryLayoutHoverRenderKind::Both => {
1218 format_to!(label, "{padding}")
1219 }
1220 }
1221 format_to!(label, ", ");
1222 }
1223
1224 if config.niches
1225 && let Some(niches) = layout.niches()
1226 {
1227 if niches > 1024 {
1228 if niches.is_power_of_two() {
1229 format_to!(label, "niches = 2{}, ", pwr2_to_exponent(niches));
1230 } else if is_pwr2plus1(niches) {
1231 format_to!(label, "niches = 2{} + 1, ", pwr2_to_exponent(niches - 1));
1232 } else if is_pwr2minus1(niches) {
1233 format_to!(label, "niches = 2{} - 1, ", pwr2_to_exponent(niches + 1));
1234 } else {
1235 format_to!(label, "niches = a lot, ");
1236 }
1237 } else {
1238 format_to!(label, "niches = {niches}, ");
1239 }
1240 }
1241 label.pop(); label.pop(); Some(label)
1244}
1245
1246struct KeywordHint {
1247 description: String,
1248 keyword_mod: String,
1249 actions: Vec<HoverAction>,
1250}
1251
1252impl KeywordHint {
1253 fn new(description: String, keyword_mod: String) -> Self {
1254 Self { description, keyword_mod, actions: Vec::default() }
1255 }
1256}
1257
1258fn keyword_hints(
1259 sema: &Semantics<'_, RootDatabase>,
1260 token: &SyntaxToken,
1261 parent: syntax::SyntaxNode,
1262 edition: Edition,
1263 display_target: DisplayTarget,
1264) -> KeywordHint {
1265 match token.kind() {
1266 T![await] | T![loop] | T![match] | T![unsafe] | T![as] | T![try] | T![if] | T![else] => {
1267 let keyword_mod = format!("{}_keyword", token.text());
1268
1269 match ast::Expr::cast(parent).and_then(|site| sema.type_of_expr(&site)) {
1270 Some(ty) if !ty.adjusted.as_ref().unwrap_or(&ty.original).is_unit() => {
1272 let mut targets: Vec<hir::ModuleDef> = Vec::new();
1273 let mut push_new_def = |item: hir::ModuleDef| {
1274 if !targets.contains(&item) {
1275 targets.push(item);
1276 }
1277 };
1278 walk_and_push_ty(sema.db, &ty.original, &mut push_new_def);
1279
1280 let ty = ty.adjusted();
1281 let description =
1282 format!("{}: {}", token.text(), ty.display(sema.db, display_target));
1283
1284 KeywordHint {
1285 description,
1286 keyword_mod,
1287 actions: HoverAction::goto_type_from_targets(sema, targets, edition)
1288 .into_iter()
1289 .collect(),
1290 }
1291 }
1292 _ => KeywordHint {
1293 description: token.text().to_owned(),
1294 keyword_mod,
1295 actions: Vec::new(),
1296 },
1297 }
1298 }
1299 T![fn] => {
1300 let module = match ast::FnPtrType::cast(parent) {
1301 Some(_) => format!("prim_{}", token.text()),
1303 None => format!("{}_keyword", token.text()),
1304 };
1305 KeywordHint::new(token.text().to_owned(), module)
1306 }
1307 T![Self] => KeywordHint::new(token.text().to_owned(), "self_upper_keyword".into()),
1308 _ => KeywordHint::new(token.text().to_owned(), format!("{}_keyword", token.text())),
1309 }
1310}
1311
1312fn render_dyn_compatibility(
1313 db: &RootDatabase,
1314 buf: &mut String,
1315 safety: Option<DynCompatibilityViolation>,
1316) {
1317 let Some(osv) = safety else {
1318 buf.push_str("Is dyn-compatible");
1319 return;
1320 };
1321 buf.push_str("Is not dyn-compatible due to ");
1322 match osv {
1323 DynCompatibilityViolation::SizedSelf => {
1324 buf.push_str("having a `Self: Sized` bound");
1325 }
1326 DynCompatibilityViolation::SelfReferential => {
1327 buf.push_str("having a bound that references `Self`");
1328 }
1329 DynCompatibilityViolation::Method(func, mvc) => {
1330 let name = hir::Function::from(func).name(db);
1331 format_to!(buf, "having a method `{}` that is not dispatchable due to ", name.as_str());
1332 let desc = match mvc {
1333 MethodViolationCode::StaticMethod => "missing a receiver",
1334 MethodViolationCode::ReferencesSelfInput => "having a parameter referencing `Self`",
1335 MethodViolationCode::ReferencesSelfOutput => "the return type referencing `Self`",
1336 MethodViolationCode::ReferencesImplTraitInTrait => {
1337 "the return type containing `impl Trait`"
1338 }
1339 MethodViolationCode::AsyncFn => "being async",
1340 MethodViolationCode::WhereClauseReferencesSelf => {
1341 "a where clause referencing `Self`"
1342 }
1343 MethodViolationCode::Generic => "having a const or type generic parameter",
1344 MethodViolationCode::UndispatchableReceiver => {
1345 "having a non-dispatchable receiver type"
1346 }
1347 };
1348 buf.push_str(desc);
1349 }
1350 DynCompatibilityViolation::AssocConst(const_) => {
1351 let name = hir::Const::from(const_).name(db);
1352 if let Some(name) = name {
1353 format_to!(buf, "having an associated constant `{}`", name.as_str());
1354 } else {
1355 buf.push_str("having an associated constant");
1356 }
1357 }
1358 DynCompatibilityViolation::GAT(alias) => {
1359 let name = hir::TypeAlias::from(alias).name(db);
1360 format_to!(buf, "having a generic associated type `{}`", name.as_str());
1361 }
1362 DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait) => {
1363 let name = hir::Trait::from(super_trait).name(db);
1364 format_to!(buf, "having a dyn-incompatible supertrait `{}`", name.as_str());
1365 }
1366 }
1367}
1368
1369fn is_pwr2minus1(val: u128) -> bool {
1370 val == u128::MAX || (val + 1).is_power_of_two()
1371}
1372
1373fn is_pwr2plus1(val: u128) -> bool {
1374 val != 0 && (val - 1).is_power_of_two()
1375}
1376
1377fn pwr2_to_exponent(num: u128) -> String {
1380 const DIGITS: [char; 10] = ['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'];
1381 assert_eq!(num.count_ones(), 1);
1382 num.trailing_zeros()
1383 .to_string()
1384 .chars()
1385 .map(|c| c.to_digit(10).unwrap() as usize)
1386 .map(|idx| DIGITS[idx])
1387 .collect::<String>()
1388}
1389
1390#[cfg(test)]
1391mod tests {
1392 use super::*;
1393
1394 const TESTERS: [u128; 10] = [0, 1, 2, 3, 4, 255, 256, 257, u128::MAX - 1, u128::MAX];
1395
1396 #[test]
1397 fn test_is_pwr2minus1() {
1398 const OUTCOMES: [bool; 10] =
1399 [true, true, false, true, false, true, false, false, false, true];
1400 for (test, expected) in TESTERS.iter().zip(OUTCOMES) {
1401 let actual = is_pwr2minus1(*test);
1402 assert_eq!(actual, expected, "is_pwr2minu1({test}) gave {actual}, expected {expected}");
1403 }
1404 }
1405
1406 #[test]
1407 fn test_is_pwr2plus1() {
1408 const OUTCOMES: [bool; 10] =
1409 [false, false, true, true, false, false, false, true, false, false];
1410 for (test, expected) in TESTERS.iter().zip(OUTCOMES) {
1411 let actual = is_pwr2plus1(*test);
1412 assert_eq!(actual, expected, "is_pwr2plus1({test}) gave {actual}, expected {expected}");
1413 }
1414 }
1415
1416 #[test]
1417 fn test_pwr2_to_exponent() {
1418 const TESTERS: [u128; 9] = [
1419 1,
1420 2,
1421 4,
1422 8,
1423 16,
1424 9223372036854775808,
1425 18446744073709551616,
1426 36893488147419103232,
1427 170141183460469231731687303715884105728,
1428 ];
1429 const OUTCOMES: [&str; 9] = ["⁰", "¹", "²", "³", "⁴", "⁶³", "⁶⁴", "⁶⁵", "¹²⁷"];
1430 for (test, expected) in TESTERS.iter().zip(OUTCOMES) {
1431 let actual = pwr2_to_exponent(*test);
1432 assert_eq!(
1433 actual, expected,
1434 "pwr2_to_exponent({test}) returned {actual}, expected {expected}",
1435 );
1436 }
1437 }
1438}