Skip to content

Commit 00efd83

Browse files
GearsDatapackslpil
authored andcommitted
Fix generate function code action for unsupported targets
1 parent 1ecf827 commit 00efd83

File tree

6 files changed

+68
-52
lines changed

6 files changed

+68
-52
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
## Unreleased
44

5+
### Bug fixes
6+
7+
- Fixed a bug where the "Generate function" code action would be incorrectly
8+
offered when calling a function unsupported by the current target, leading to
9+
invalid code if the code action was accepted.
10+
([Surya Rose](https://github.com/GearsDatapacks))
11+
512
### Compiler
613

714
- Patterns aliasing a string prefix have been optimised to generate faster code

compiler-core/src/ast/typed.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,4 +1182,7 @@ pub enum InvalidExpression {
11821182
module_name: EcoString,
11831183
label: EcoString,
11841184
},
1185+
UnknownVariable {
1186+
name: EcoString,
1187+
},
11851188
}

compiler-core/src/language_server/code_action.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5305,15 +5305,13 @@ impl<'a> GenerateFunction<'a> {
53055305

53065306
fn try_save_function_to_generate(
53075307
&mut self,
5308-
function_name_location: SrcSpan,
5308+
name: &'a EcoString,
53095309
function_type: &Arc<Type>,
53105310
given_arguments: Option<&'a [TypedCallArg]>,
53115311
) {
5312-
let candidate_name = code_at(self.module, function_name_location);
5313-
match (candidate_name, function_type.fn_types()) {
5314-
(_, None) => (),
5315-
(name, _) if !is_valid_lowercase_name(name) => (),
5316-
(name, Some((arguments_types, return_type))) => {
5312+
match function_type.fn_types() {
5313+
None => {}
5314+
Some((arguments_types, return_type)) => {
53175315
self.function_to_generate = Some(FunctionToGenerate {
53185316
name,
53195317
arguments_types,
@@ -5366,7 +5364,10 @@ impl<'ast> ast::visit::Visit<'ast> for GenerateFunction<'ast> {
53665364
Some(InvalidExpression::ModuleSelect { module_name, label }) => {
53675365
self.try_save_function_from_other_module(module_name, label, type_, None)
53685366
}
5369-
None => self.try_save_function_to_generate(*location, type_, None),
5367+
Some(InvalidExpression::UnknownVariable { name }) => {
5368+
self.try_save_function_to_generate(name, type_, None)
5369+
}
5370+
None => {}
53705371
}
53715372
}
53725373

@@ -5404,10 +5405,10 @@ impl<'ast> ast::visit::Visit<'ast> for GenerateFunction<'ast> {
54045405
}
54055406
TypedExpr::Invalid {
54065407
type_,
5407-
location,
5408-
extra_information: _,
5408+
extra_information: Some(InvalidExpression::UnknownVariable { name }),
5409+
location: _,
54095410
} => {
5410-
return self.try_save_function_to_generate(*location, type_, Some(arguments));
5411+
return self.try_save_function_to_generate(name, type_, Some(arguments));
54115412
}
54125413
_ => {}
54135414
}

compiler-core/src/language_server/tests/action.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10727,3 +10727,20 @@ pub fn main() -> Nil {
1072710727
find_position_of("function").to_selection()
1072810728
);
1072910729
}
10730+
10731+
// https://github.com/gleam-lang/gleam/issues/4904
10732+
#[test]
10733+
fn no_code_action_to_generate_function_on_unsupported_target() {
10734+
assert_no_code_actions!(
10735+
GENERATE_FUNCTION,
10736+
r#"
10737+
pub fn main() {
10738+
wibble()
10739+
}
10740+
10741+
@external(javascript, "./ffi.mjs", "wibble")
10742+
fn wibble() -> Nil
10743+
"#,
10744+
find_position_of("wibble").to_selection()
10745+
);
10746+
}

compiler-core/src/type_/expression.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,18 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
713713
}
714714
}
715715

716+
fn error_expr_with_information(
717+
&mut self,
718+
location: SrcSpan,
719+
extra_information: Option<InvalidExpression>,
720+
) -> TypedExpr {
721+
TypedExpr::Invalid {
722+
location,
723+
type_: self.new_unbound_var(),
724+
extra_information,
725+
}
726+
}
727+
716728
fn infer_iter_statements<StatementsIter: Iterator<Item = UntypedStatement>>(
717729
&mut self,
718730
location: SrcSpan,
@@ -1713,8 +1725,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
17131725
match self.infer_or_error(expression) {
17141726
Ok(result) => result,
17151727
Err(error) => {
1728+
let information = match &error {
1729+
Error::UnknownVariable { name, .. } => {
1730+
Some(InvalidExpression::UnknownVariable { name: name.clone() })
1731+
}
1732+
_ => None,
1733+
};
1734+
17161735
self.problems.error(error);
1717-
self.error_expr(location)
1736+
self.error_expr_with_information(location, information)
17181737
}
17191738
}
17201739
}
@@ -4542,11 +4561,16 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
45424561
ReferenceRegistration::Register
45434562
};
45444563

4545-
match self.infer_var(argument_name, argument_location, references) {
4564+
match self.infer_var(argument_name.clone(), argument_location, references) {
45464565
Ok(result) => result,
45474566
Err(error) => {
45484567
self.problems.error(error);
4549-
self.error_expr(argument_location)
4568+
self.error_expr_with_information(
4569+
argument_location,
4570+
Some(InvalidExpression::UnknownVariable {
4571+
name: argument_name,
4572+
}),
4573+
)
45504574
}
45514575
}
45524576
}

compiler-core/src/type_/pipe.rs

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,7 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> {
5555
let end = expressions.last().location().end;
5656
let mut expressions = expressions.into_iter();
5757
let first = expressions.next().expect("Empty pipeline in typer");
58-
let first_location = first.location();
59-
let first = match expr_typer.infer_or_error(first) {
60-
Ok(inferred) => inferred,
61-
Err(e) => {
62-
expr_typer.problems.error(e);
63-
TypedExpr::Invalid {
64-
location: first_location,
65-
type_: expr_typer.new_unbound_var(),
66-
extra_information: None,
67-
}
68-
}
69-
};
58+
let first = expr_typer.infer(first);
7059

7160
Self::new(expr_typer, size, first, end).infer_expressions(expressions)
7261
}
@@ -140,20 +129,7 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> {
140129
location,
141130
..
142131
} => {
143-
let fun = match self.expr_typer.infer_or_error(*fun) {
144-
Ok(fun) => fun,
145-
Err(e) => {
146-
// In case we cannot infer the function we'll
147-
// replace it with an invalid expression with an
148-
// unbound type to keep going!
149-
self.expr_typer.problems.error(e);
150-
TypedExpr::Invalid {
151-
location,
152-
type_: self.expr_typer.new_unbound_var(),
153-
extra_information: None,
154-
}
155-
}
156-
};
132+
let fun = self.expr_typer.infer(*fun);
157133

158134
match fun.type_().fn_types() {
159135
// Rewrite as right(..args)(left)
@@ -352,19 +328,7 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> {
352328
/// b is the `function` argument.
353329
fn infer_apply_pipe(&mut self, function: UntypedExpr) -> TypedExpr {
354330
let function_location = function.location();
355-
let function = Box::new(match self.expr_typer.infer_or_error(function) {
356-
Ok(function) => function,
357-
Err(error) => {
358-
// If we cannot infer the function we put an invalid expression
359-
// in its place so we can still keep going with the other steps.
360-
self.expr_typer.problems.error(error);
361-
TypedExpr::Invalid {
362-
location: function_location,
363-
type_: self.expr_typer.new_unbound_var(),
364-
extra_information: None,
365-
}
366-
}
367-
});
331+
let function = Box::new(self.expr_typer.infer(function));
368332

369333
self.expr_typer.purity = self
370334
.expr_typer

0 commit comments

Comments
 (0)