Merge pull request #18373 from Veykril/veykril/push-mzumrrvynxqu

internal: Merge separate inlay hints targeting same range
This commit is contained in:
Lukas Wirth 2024-10-22 15:28:28 +00:00 committed by GitHub
commit b35d93c471
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 153 additions and 246 deletions

View file

@ -475,6 +475,18 @@ impl InlayHintLabel {
} }
} }
pub fn append_part(&mut self, part: InlayHintLabelPart) {
if part.linked_location.is_none() && part.tooltip.is_none() {
if let Some(InlayHintLabelPart { text, linked_location: None, tooltip: None }) =
self.parts.last_mut()
{
text.push_str(&part.text);
return;
}
}
self.parts.push(part);
}
pub fn needs_resolve(&self) -> bool { pub fn needs_resolve(&self) -> bool {
self.parts.iter().any(|part| part.linked_location.is_some() || part.tooltip.is_some()) self.parts.iter().any(|part| part.linked_location.is_some() || part.tooltip.is_some())
} }

View file

@ -17,8 +17,8 @@ use syntax::{
}; };
use crate::{ use crate::{
AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintPosition, AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintLabelPart,
InlayHintsConfig, InlayKind, InlayTooltip, InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip,
}; };
pub(super) fn hints( pub(super) fn hints(
@ -64,19 +64,34 @@ pub(super) fn hints(
let (postfix, needs_outer_parens, needs_inner_parens) = let (postfix, needs_outer_parens, needs_inner_parens) =
mode_and_needs_parens_for_adjustment_hints(expr, config.adjustment_hints_mode); mode_and_needs_parens_for_adjustment_hints(expr, config.adjustment_hints_mode);
if needs_outer_parens { let range = expr.syntax().text_range();
acc.push(InlayHint::opening_paren_before( let mut pre = InlayHint {
InlayKind::Adjustment, range,
expr.syntax().text_range(), position: InlayHintPosition::Before,
)); pad_left: false,
pad_right: false,
kind: InlayKind::Adjustment,
label: InlayHintLabel::default(),
text_edit: None,
resolve_parent: Some(range),
};
let mut post = InlayHint {
range,
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
kind: InlayKind::Adjustment,
label: InlayHintLabel::default(),
text_edit: None,
resolve_parent: Some(range),
};
if needs_outer_parens || (postfix && needs_inner_parens) {
pre.label.append_str("(");
} }
if postfix && needs_inner_parens { if postfix && needs_inner_parens {
acc.push(InlayHint::opening_paren_before( post.label.append_str(")");
InlayKind::Adjustment,
expr.syntax().text_range(),
));
acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
} }
let mut iter = if postfix { let mut iter = if postfix {
@ -138,35 +153,28 @@ pub(super) fn hints(
} }
_ => continue, _ => continue,
}; };
let label = InlayHintLabel::simple( let label = InlayHintLabelPart {
if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() }, text: if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
Some(InlayTooltip::Markdown(format!( linked_location: None,
tooltip: Some(InlayTooltip::Markdown(format!(
"`{}` → `{}` ({coercion} coercion)", "`{}` → `{}` ({coercion} coercion)",
source.display(sema.db, file_id.edition()), source.display(sema.db, file_id.edition()),
target.display(sema.db, file_id.edition()), target.display(sema.db, file_id.edition()),
))), ))),
None, };
); if postfix { &mut post } else { &mut pre }.label.append_part(label);
acc.push(InlayHint {
range: expr.syntax().text_range(),
pad_left: false,
pad_right: false,
position: if postfix { InlayHintPosition::After } else { InlayHintPosition::Before },
kind: InlayKind::Adjustment,
label,
text_edit: None,
resolve_parent: Some(expr.syntax().text_range()),
});
} }
if !postfix && needs_inner_parens { if !postfix && needs_inner_parens {
acc.push(InlayHint::opening_paren_before( pre.label.append_str("(");
InlayKind::Adjustment,
expr.syntax().text_range(),
));
acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
} }
if needs_outer_parens { if needs_outer_parens || (!postfix && needs_inner_parens) {
acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range())); post.label.append_str(")");
}
if !pre.label.parts.is_empty() {
acc.push(pre);
}
if !post.label.parts.is_empty() {
acc.push(post);
} }
Some(()) Some(())
} }
@ -293,25 +301,19 @@ fn main() {
let _: u32 = loop {}; let _: u32 = loop {};
//^^^^^^^<never-to-any> //^^^^^^^<never-to-any>
let _: &u32 = &mut 0; let _: &u32 = &mut 0;
//^^^^^^& //^^^^^^&*
//^^^^^^*
let _: &mut u32 = &mut 0; let _: &mut u32 = &mut 0;
//^^^^^^&mut $ //^^^^^^&mut *
//^^^^^^*
let _: *const u32 = &mut 0; let _: *const u32 = &mut 0;
//^^^^^^&raw const $ //^^^^^^&raw const *
//^^^^^^*
let _: *mut u32 = &mut 0; let _: *mut u32 = &mut 0;
//^^^^^^&raw mut $ //^^^^^^&raw mut *
//^^^^^^*
let _: fn() = main; let _: fn() = main;
//^^^^<fn-item-to-fn-pointer> //^^^^<fn-item-to-fn-pointer>
let _: unsafe fn() = main; let _: unsafe fn() = main;
//^^^^<safe-fn-pointer-to-unsafe-fn-pointer> //^^^^<safe-fn-pointer-to-unsafe-fn-pointer><fn-item-to-fn-pointer>
//^^^^<fn-item-to-fn-pointer>
let _: unsafe fn() = main as fn(); let _: unsafe fn() = main as fn();
//^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer> //^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer>(
//^^^^^^^^^^^^(
//^^^^^^^^^^^^) //^^^^^^^^^^^^)
//^^^^<fn-item-to-fn-pointer> //^^^^<fn-item-to-fn-pointer>
let _: fn() = || {}; let _: fn() = || {};
@ -319,72 +321,51 @@ fn main() {
let _: unsafe fn() = || {}; let _: unsafe fn() = || {};
//^^^^^<closure-to-unsafe-fn-pointer> //^^^^^<closure-to-unsafe-fn-pointer>
let _: *const u32 = &mut 0u32 as *mut u32; let _: *const u32 = &mut 0u32 as *mut u32;
//^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr> //^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr>(
//^^^^^^^^^^^^^^^^^^^^^(
//^^^^^^^^^^^^^^^^^^^^^) //^^^^^^^^^^^^^^^^^^^^^)
//^^^^^^^^^&raw mut $ //^^^^^^^^^&raw mut *
//^^^^^^^^^*
let _: &mut [_] = &mut [0; 0]; let _: &mut [_] = &mut [0; 0];
//^^^^^^^^^^^<unsize> //^^^^^^^^^^^<unsize>&mut *
//^^^^^^^^^^^&mut $
//^^^^^^^^^^^*
Struct.consume(); Struct.consume();
Struct.by_ref(); Struct.by_ref();
//^^^^^^( //^^^^^^(&
//^^^^^^&
//^^^^^^) //^^^^^^)
Struct.by_ref_mut(); Struct.by_ref_mut();
//^^^^^^( //^^^^^^(&mut $
//^^^^^^&mut $
//^^^^^^) //^^^^^^)
(&Struct).consume(); (&Struct).consume();
//^^^^^^^* //^^^^^^^*
(&Struct).by_ref(); (&Struct).by_ref();
//^^^^^^^& //^^^^^^^&*
//^^^^^^^*
(&mut Struct).consume(); (&mut Struct).consume();
//^^^^^^^^^^^* //^^^^^^^^^^^*
(&mut Struct).by_ref(); (&mut Struct).by_ref();
//^^^^^^^^^^^& //^^^^^^^^^^^&*
//^^^^^^^^^^^*
(&mut Struct).by_ref_mut(); (&mut Struct).by_ref_mut();
//^^^^^^^^^^^&mut $ //^^^^^^^^^^^&mut *
//^^^^^^^^^^^*
// Check that block-like expressions don't duplicate hints // Check that block-like expressions don't duplicate hints
let _: &mut [u32] = (&mut []); let _: &mut [u32] = (&mut []);
//^^^^^^^<unsize> //^^^^^^^<unsize>&mut *
//^^^^^^^&mut $
//^^^^^^^*
let _: &mut [u32] = { &mut [] }; let _: &mut [u32] = { &mut [] };
//^^^^^^^<unsize> //^^^^^^^<unsize>&mut *
//^^^^^^^&mut $
//^^^^^^^*
let _: &mut [u32] = unsafe { &mut [] }; let _: &mut [u32] = unsafe { &mut [] };
//^^^^^^^<unsize> //^^^^^^^<unsize>&mut *
//^^^^^^^&mut $
//^^^^^^^*
let _: &mut [u32] = if true { let _: &mut [u32] = if true {
&mut [] &mut []
//^^^^^^^<unsize> //^^^^^^^<unsize>&mut *
//^^^^^^^&mut $
//^^^^^^^*
} else { } else {
loop {} loop {}
//^^^^^^^<never-to-any> //^^^^^^^<never-to-any>
}; };
let _: &mut [u32] = match () { () => &mut [] }; let _: &mut [u32] = match () { () => &mut [] };
//^^^^^^^<unsize> //^^^^^^^<unsize>&mut *
//^^^^^^^&mut $
//^^^^^^^*
let _: &mut dyn Fn() = &mut || (); let _: &mut dyn Fn() = &mut || ();
//^^^^^^^^^^<unsize> //^^^^^^^^^^<unsize>&mut *
//^^^^^^^^^^&mut $
//^^^^^^^^^^*
() == (); () == ();
// ^^& // ^^&
// ^^& // ^^&
@ -393,16 +374,13 @@ fn main() {
// ^^^^& // ^^^^&
let closure: dyn Fn = || (); let closure: dyn Fn = || ();
closure(); closure();
//^^^^^^^( //^^^^^^^(&
//^^^^^^^&
//^^^^^^^) //^^^^^^^)
Struct[0]; Struct[0];
//^^^^^^( //^^^^^^(&
//^^^^^^&
//^^^^^^) //^^^^^^)
&mut Struct[0]; &mut Struct[0];
//^^^^^^( //^^^^^^(&mut $
//^^^^^^&mut $
//^^^^^^) //^^^^^^)
} }
@ -442,72 +420,46 @@ fn main() {
(&Struct).consume(); (&Struct).consume();
//^^^^^^^( //^^^^^^^(
//^^^^^^^) //^^^^^^^).*
//^^^^^^^.*
(&Struct).by_ref(); (&Struct).by_ref();
//^^^^^^^( //^^^^^^^(
//^^^^^^^) //^^^^^^^).*.&
//^^^^^^^.*
//^^^^^^^.&
(&mut Struct).consume(); (&mut Struct).consume();
//^^^^^^^^^^^( //^^^^^^^^^^^(
//^^^^^^^^^^^) //^^^^^^^^^^^).*
//^^^^^^^^^^^.*
(&mut Struct).by_ref(); (&mut Struct).by_ref();
//^^^^^^^^^^^( //^^^^^^^^^^^(
//^^^^^^^^^^^) //^^^^^^^^^^^).*.&
//^^^^^^^^^^^.*
//^^^^^^^^^^^.&
(&mut Struct).by_ref_mut(); (&mut Struct).by_ref_mut();
//^^^^^^^^^^^( //^^^^^^^^^^^(
//^^^^^^^^^^^) //^^^^^^^^^^^).*.&mut
//^^^^^^^^^^^.*
//^^^^^^^^^^^.&mut
// Check that block-like expressions don't duplicate hints // Check that block-like expressions don't duplicate hints
let _: &mut [u32] = (&mut []); let _: &mut [u32] = (&mut []);
//^^^^^^^( //^^^^^^^(
//^^^^^^^) //^^^^^^^).*.&mut.<unsize>
//^^^^^^^.*
//^^^^^^^.&mut
//^^^^^^^.<unsize>
let _: &mut [u32] = { &mut [] }; let _: &mut [u32] = { &mut [] };
//^^^^^^^( //^^^^^^^(
//^^^^^^^) //^^^^^^^).*.&mut.<unsize>
//^^^^^^^.*
//^^^^^^^.&mut
//^^^^^^^.<unsize>
let _: &mut [u32] = unsafe { &mut [] }; let _: &mut [u32] = unsafe { &mut [] };
//^^^^^^^( //^^^^^^^(
//^^^^^^^) //^^^^^^^).*.&mut.<unsize>
//^^^^^^^.*
//^^^^^^^.&mut
//^^^^^^^.<unsize>
let _: &mut [u32] = if true { let _: &mut [u32] = if true {
&mut [] &mut []
//^^^^^^^( //^^^^^^^(
//^^^^^^^) //^^^^^^^).*.&mut.<unsize>
//^^^^^^^.*
//^^^^^^^.&mut
//^^^^^^^.<unsize>
} else { } else {
loop {} loop {}
//^^^^^^^.<never-to-any> //^^^^^^^.<never-to-any>
}; };
let _: &mut [u32] = match () { () => &mut [] }; let _: &mut [u32] = match () { () => &mut [] };
//^^^^^^^( //^^^^^^^(
//^^^^^^^) //^^^^^^^).*.&mut.<unsize>
//^^^^^^^.*
//^^^^^^^.&mut
//^^^^^^^.<unsize>
let _: &mut dyn Fn() = &mut || (); let _: &mut dyn Fn() = &mut || ();
//^^^^^^^^^^( //^^^^^^^^^^(
//^^^^^^^^^^) //^^^^^^^^^^).*.&mut.<unsize>
//^^^^^^^^^^.*
//^^^^^^^^^^.&mut
//^^^^^^^^^^.<unsize>
() == (); () == ();
// ^^.& // ^^.&
// ^^.& // ^^.&
@ -619,9 +571,7 @@ fn or_else() {
r#" r#"
unsafe fn enabled() { unsafe fn enabled() {
f(&&()); f(&&());
//^^^^& //^^^^&**
//^^^^*
//^^^^*
} }
fn disabled() { fn disabled() {
@ -633,9 +583,7 @@ fn mixed() {
unsafe { unsafe {
f(&&()); f(&&());
//^^^^& //^^^^&**
//^^^^*
//^^^^*
} }
} }
@ -644,9 +592,7 @@ const _: () = {
unsafe { unsafe {
f(&&()); f(&&());
//^^^^& //^^^^&**
//^^^^*
//^^^^*
} }
}; };
@ -655,18 +601,14 @@ static STATIC: () = {
unsafe { unsafe {
f(&&()); f(&&());
//^^^^& //^^^^&**
//^^^^*
//^^^^*
} }
}; };
enum E { enum E {
Disable = { f(&&()); 0 }, Disable = { f(&&()); 0 },
Enable = unsafe { f(&&()); 1 }, Enable = unsafe { f(&&()); 1 },
//^^^^& //^^^^&**
//^^^^*
//^^^^*
} }
const fn f(_: &()) {} const fn f(_: &()) {}
@ -692,8 +634,7 @@ fn a() {
_ = Struct.by_ref(); _ = Struct.by_ref();
_ = unsafe { Struct.by_ref() }; _ = unsafe { Struct.by_ref() };
//^^^^^^( //^^^^^^(&
//^^^^^^&
//^^^^^^) //^^^^^^)
} }
"#, "#,
@ -726,10 +667,7 @@ trait T<RHS = Self> {}
fn hello(it: &&[impl T]) { fn hello(it: &&[impl T]) {
it.len(); it.len();
//^^( //^^(&**
//^^&
//^^*
//^^*
//^^) //^^)
} }
"#, "#,

View file

@ -2,13 +2,15 @@
//! ```no_run //! ```no_run
//! let /* & */ (/* ref */ x,) = &(0,); //! let /* & */ (/* ref */ x,) = &(0,);
//! ``` //! ```
use std::mem;
use hir::Mutability; use hir::Mutability;
use ide_db::famous_defs::FamousDefs; use ide_db::famous_defs::FamousDefs;
use span::EditionedFileId; use span::EditionedFileId;
use syntax::ast::{self, AstNode}; use syntax::ast::{self, AstNode};
use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
pub(super) fn hints( pub(super) fn hints(
acc: &mut Vec<InlayHint>, acc: &mut Vec<InlayHint>,
@ -42,7 +44,18 @@ pub(super) fn hints(
}, },
|it| it.syntax().text_range(), |it| it.syntax().text_range(),
); );
let mut hint = InlayHint {
range,
kind: InlayKind::BindingMode,
label: InlayHintLabel::default(),
text_edit: None,
position: InlayHintPosition::Before,
pad_left: false,
pad_right: false,
resolve_parent: Some(pat.syntax().text_range()),
};
let pattern_adjustments = sema.pattern_adjustments(pat); let pattern_adjustments = sema.pattern_adjustments(pat);
let mut was_mut_last = false;
pattern_adjustments.iter().for_each(|ty| { pattern_adjustments.iter().for_each(|ty| {
let reference = ty.is_reference(); let reference = ty.is_reference();
let mut_reference = ty.is_mutable_reference(); let mut_reference = ty.is_mutable_reference();
@ -51,17 +64,15 @@ pub(super) fn hints(
(true, false) => "&", (true, false) => "&",
_ => return, _ => return,
}; };
acc.push(InlayHint { if mem::replace(&mut was_mut_last, mut_reference) {
range, hint.label.append_str(" ");
kind: InlayKind::BindingMode, }
label: r.into(), hint.label.append_str(r);
text_edit: None,
position: InlayHintPosition::Before,
pad_left: false,
pad_right: mut_reference,
resolve_parent: Some(pat.syntax().text_range()),
});
}); });
hint.pad_right = was_mut_last;
if !hint.label.parts.is_empty() {
acc.push(hint);
}
match pat { match pat {
ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => { ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
let bm = sema.binding_mode_of_pat(pat)?; let bm = sema.binding_mode_of_pat(pat)?;
@ -117,6 +128,13 @@ fn __(
(x,): &mut (u32,) (x,): &mut (u32,)
//^^^^&mut //^^^^&mut
//^ ref mut //^ ref mut
(x,): &mut &mut (u32,)
//^^^^&mut &mut
//^ ref mut
(x,): &&(u32,)
//^^^^&&
//^ ref
) { ) {
let (x,) = (0,); let (x,) = (0,);
let (x,) = &(0,); let (x,) = &(0,);

View file

@ -7,7 +7,9 @@ use stdx::{never, TupleExt};
use syntax::ast::{self, AstNode}; use syntax::ast::{self, AstNode};
use text_edit::{TextRange, TextSize}; use text_edit::{TextRange, TextSize};
use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; use crate::{
InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind,
};
pub(super) fn hints( pub(super) fn hints(
acc: &mut Vec<InlayHint>, acc: &mut Vec<InlayHint>,
@ -27,34 +29,27 @@ pub(super) fn hints(
return None; return None;
} }
let move_kw_range = match closure.move_token() { let (range, label) = match closure.move_token() {
Some(t) => t.text_range(), Some(t) => (t.text_range(), InlayHintLabel::default()),
None => { None => {
let range = closure.syntax().first_token()?.prev_token()?.text_range(); let prev_token = closure.syntax().first_token()?.prev_token()?.text_range();
let range = TextRange::new(range.end() - TextSize::from(1), range.end()); (
acc.push(InlayHint { TextRange::new(prev_token.end() - TextSize::from(1), prev_token.end()),
range, InlayHintLabel::from("move"),
kind: InlayKind::ClosureCapture, )
label: InlayHintLabel::from("move"),
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
resolve_parent: Some(closure.syntax().text_range()),
});
range
} }
}; };
acc.push(InlayHint { let mut hint = InlayHint {
range: move_kw_range, range,
kind: InlayKind::ClosureCapture, kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from("("), label,
text_edit: None, text_edit: None,
position: InlayHintPosition::After, position: InlayHintPosition::After,
pad_left: false, pad_left: false,
pad_right: false, pad_right: true,
resolve_parent: None, resolve_parent: Some(closure.syntax().text_range()),
}); };
hint.label.append_str("(");
let last = captures.len() - 1; let last = captures.len() - 1;
for (idx, capture) in captures.into_iter().enumerate() { for (idx, capture) in captures.into_iter().enumerate() {
let local = capture.local(); let local = capture.local();
@ -76,48 +71,20 @@ pub(super) fn hints(
if never!(label.is_empty()) { if never!(label.is_empty()) {
continue; continue;
} }
let label = InlayHintLabel::simple( hint.label.append_part(InlayHintLabelPart {
label, text: label,
None, linked_location: source.name().and_then(|name| {
source.name().and_then(|name| {
name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into) name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into)
}), }),
); tooltip: None,
acc.push(InlayHint {
range: move_kw_range,
kind: InlayKind::ClosureCapture,
label,
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
resolve_parent: Some(closure.syntax().text_range()),
}); });
if idx != last { if idx != last {
acc.push(InlayHint { hint.label.append_str(", ");
range: move_kw_range,
kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from(", "),
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
resolve_parent: None,
});
} }
} }
acc.push(InlayHint { hint.label.append_str(")");
range: move_kw_range, acc.push(hint);
kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from(")"),
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
pad_right: true,
resolve_parent: None,
});
Some(()) Some(())
} }
@ -147,51 +114,25 @@ fn main() {
let mut baz = NonCopy; let mut baz = NonCopy;
let qux = &mut NonCopy; let qux = &mut NonCopy;
|| { || {
// ^ move // ^ move(&foo, bar, baz, qux)
// ^ (
// ^ &foo
// ^ , $
// ^ bar
// ^ , $
// ^ baz
// ^ , $
// ^ qux
// ^ )
foo; foo;
bar; bar;
baz; baz;
qux; qux;
}; };
|| { || {
// ^ move // ^ move(&foo, &bar, &baz, &qux)
// ^ (
// ^ &foo
// ^ , $
// ^ &bar
// ^ , $
// ^ &baz
// ^ , $
// ^ &qux
// ^ )
&foo; &foo;
&bar; &bar;
&baz; &baz;
&qux; &qux;
}; };
|| { || {
// ^ move // ^ move(&mut baz)
// ^ (
// ^ &mut baz
// ^ )
&mut baz; &mut baz;
}; };
|| { || {
// ^ move // ^ move(&mut baz, &mut *qux)
// ^ (
// ^ &mut baz
// ^ , $
// ^ &mut *qux
// ^ )
baz = NonCopy; baz = NonCopy;
*qux = NonCopy; *qux = NonCopy;
}; };
@ -209,9 +150,7 @@ fn main() {
fn main() { fn main() {
let foo = u32; let foo = u32;
move || { move || {
// ^^^^ ( // ^^^^ (foo)
// ^^^^ foo
// ^^^^ )
foo; foo;
}; };
} }