Skip to content

Commit 0761e59

Browse files
committed
Preserve annotated type-variable names in LSP "add annotations" action
1 parent 893c9f8 commit 0761e59

File tree

3 files changed

+181
-1
lines changed

3 files changed

+181
-1
lines changed

compiler-core/src/language_server/code_action.rs

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,13 +1371,187 @@ fn collect_type_variables(printer: &mut Printer<'_>, function: &ast::TypedFuncti
13711371
}
13721372

13731373
impl<'ast, 'a, 'b> ast::visit::Visit<'ast> for TypeVariableCollector<'a, 'b> {
1374+
fn visit_typed_function(&mut self, fun: &'ast ast::TypedFunction) {
1375+
for argument in fun.arguments.iter() {
1376+
if let Some(annotation) = &argument.annotation {
1377+
register_type_variables_from_annotation(
1378+
self.printer,
1379+
annotation,
1380+
argument.type_.as_ref(),
1381+
);
1382+
}
1383+
}
1384+
1385+
if let Some(annotation) = &fun.return_annotation {
1386+
register_type_variables_from_annotation(
1387+
self.printer,
1388+
annotation,
1389+
fun.return_type.as_ref(),
1390+
);
1391+
}
1392+
1393+
ast::visit::visit_typed_function(self, fun);
1394+
}
1395+
1396+
fn visit_typed_expr_fn(
1397+
&mut self,
1398+
location: &'ast SrcSpan,
1399+
type_: &'ast Arc<Type>,
1400+
kind: &'ast FunctionLiteralKind,
1401+
arguments: &'ast [TypedArg],
1402+
body: &'ast Vec1<TypedStatement>,
1403+
return_annotation: &'ast Option<ast::TypeAst>,
1404+
) {
1405+
if let Type::Fn {
1406+
arguments: argument_types,
1407+
return_: return_type,
1408+
..
1409+
} = type_.as_ref()
1410+
{
1411+
for (argument, argument_type) in arguments.iter().zip(argument_types) {
1412+
if let Some(annotation) = &argument.annotation {
1413+
register_type_variables_from_annotation(
1414+
self.printer,
1415+
annotation,
1416+
argument_type.as_ref(),
1417+
);
1418+
}
1419+
}
1420+
1421+
if let Some(annotation) = return_annotation {
1422+
register_type_variables_from_annotation(
1423+
self.printer,
1424+
annotation,
1425+
return_type.as_ref(),
1426+
);
1427+
}
1428+
}
1429+
1430+
ast::visit::visit_typed_expr_fn(
1431+
self,
1432+
location,
1433+
type_,
1434+
kind,
1435+
arguments,
1436+
body,
1437+
return_annotation,
1438+
);
1439+
}
1440+
13741441
fn visit_type_ast_var(&mut self, _location: &'ast SrcSpan, name: &'ast EcoString) {
13751442
// Register this type variable so that we don't duplicate names when
13761443
// adding annotations.
13771444
self.printer.register_type_variable(name.clone());
13781445
}
13791446
}
13801447

1448+
fn register_type_variables_from_annotation(
1449+
printer: &mut Printer<'_>,
1450+
annotation: &ast::TypeAst,
1451+
type_: &Type,
1452+
) {
1453+
// fn wibble(a, b, c) {
1454+
// fn(a: b, b: c) -> d { ... }
1455+
// ^
1456+
// Without this tracking the printer could rename `d` to a fresh `h`.
1457+
match (annotation, type_) {
1458+
(ast::TypeAst::Var(ast::TypeAstVar { name, .. }), Type::Var { type_ }) => {
1459+
match &*type_.borrow() {
1460+
TypeVar::Generic { id } | TypeVar::Unbound { id } => {
1461+
let id = *id;
1462+
printer.register_type_variable(name.clone());
1463+
printer.register_type_variable_with_id(id, name.clone());
1464+
}
1465+
TypeVar::Link { type_ } => {
1466+
register_type_variables_from_annotation(printer, annotation, type_.as_ref());
1467+
}
1468+
}
1469+
}
1470+
1471+
(
1472+
ast::TypeAst::Fn(ast::TypeAstFn {
1473+
arguments: annotation_arguments,
1474+
return_: annotation_return,
1475+
..
1476+
}),
1477+
Type::Fn {
1478+
arguments: type_arguments,
1479+
return_: type_return,
1480+
..
1481+
},
1482+
) => {
1483+
for (argument_annotation, argument_type) in
1484+
annotation_arguments.iter().zip(type_arguments)
1485+
{
1486+
// Maintain the names from each `fn(arg: name, ...)` position.
1487+
register_type_variables_from_annotation(
1488+
printer,
1489+
argument_annotation,
1490+
argument_type.as_ref(),
1491+
);
1492+
}
1493+
1494+
// And likewise propagate the annotated return variable.
1495+
register_type_variables_from_annotation(
1496+
printer,
1497+
annotation_return.as_ref(),
1498+
type_return.as_ref(),
1499+
);
1500+
}
1501+
1502+
(
1503+
ast::TypeAst::Constructor(ast::TypeAstConstructor {
1504+
arguments: annotation_arguments,
1505+
..
1506+
}),
1507+
Type::Named {
1508+
arguments: type_arguments,
1509+
..
1510+
},
1511+
) => {
1512+
for (argument_annotation, argument_type) in
1513+
annotation_arguments.iter().zip(type_arguments)
1514+
{
1515+
// Track aliases introduced inside named type arguments.
1516+
register_type_variables_from_annotation(
1517+
printer,
1518+
argument_annotation,
1519+
argument_type.as_ref(),
1520+
);
1521+
}
1522+
}
1523+
1524+
(
1525+
ast::TypeAst::Tuple(ast::TypeAstTuple {
1526+
elements: annotation_elements,
1527+
..
1528+
}),
1529+
Type::Tuple {
1530+
elements: type_elements,
1531+
..
1532+
},
1533+
) => {
1534+
for (element_annotation, element_type) in annotation_elements.iter().zip(type_elements)
1535+
{
1536+
// Tuples can hide extra annotations; ensure each slot retains its label.
1537+
register_type_variables_from_annotation(
1538+
printer,
1539+
element_annotation,
1540+
element_type.as_ref(),
1541+
);
1542+
}
1543+
}
1544+
1545+
(_, Type::Var { type_ }) => {
1546+
if let TypeVar::Link { type_ } = &*type_.borrow() {
1547+
register_type_variables_from_annotation(printer, annotation, type_.as_ref());
1548+
}
1549+
}
1550+
1551+
_ => {}
1552+
}
1553+
}
1554+
13811555
pub struct QualifiedConstructor<'a> {
13821556
import: &'a Import<EcoString>,
13831557
used_name: EcoString,

compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__type_variables_in_let_bindings_are_considered_when_adding_annotations.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fn wibble(a, b, c) {
1515

1616
----- AFTER ACTION
1717

18-
fn wibble(a: e, b: f, c: g) -> fn(b, c) -> h {
18+
fn wibble(a: e, b: f, c: g) -> fn(b, c) -> d {
1919
let x: a = todo
2020
fn(a: b, b: c) -> d {
2121
todo

compiler-core/src/type_/printer.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,12 @@ impl<'a> Printer<'a> {
454454
_ = self.printed_type_variable_names.insert(name);
455455
}
456456

457+
/// Record that the given type-variable id is already using the supplied name.
458+
pub fn register_type_variable_with_id(&mut self, id: u64, name: EcoString) {
459+
_ = self.printed_type_variable_names.insert(name.clone());
460+
_ = self.printed_type_variables.insert(id, name);
461+
}
462+
457463
pub fn print_type(&mut self, type_: &Type) -> EcoString {
458464
let mut buffer = EcoString::new();
459465
self.print(type_, &mut buffer, PrintMode::Normal);

0 commit comments

Comments
 (0)