Skip to content

Commit b419a13

Browse files
giacomocavalierilpil
authored andcommitted
change implementation
1 parent 86b1fdc commit b419a13

8 files changed

+117
-116
lines changed

compiler-core/src/type_.rs

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -877,49 +877,6 @@ impl ValueConstructorVariant {
877877
ValueConstructorVariant::Record { field_map, .. } => field_map.as_ref(),
878878
}
879879
}
880-
881-
/// If this is the constructor of a module function, returns the function's
882-
/// module and name.
883-
///
884-
fn function_module_and_name(&self) -> Option<(EcoString, EcoString)> {
885-
match self {
886-
ValueConstructorVariant::ModuleFn { name, module, .. } => {
887-
Some((module.clone(), name.clone()))
888-
}
889-
890-
ValueConstructorVariant::ModuleConstant { .. }
891-
| ValueConstructorVariant::LocalConstant { .. }
892-
| ValueConstructorVariant::LocalVariable { .. }
893-
| ValueConstructorVariant::Record { .. } => None,
894-
}
895-
}
896-
897-
/// If this is a constructor for an argument, returns the name of the
898-
/// function where it is defined and its index in the function's parameter
899-
/// list.
900-
///
901-
fn argument_function_and_index(&self) -> Option<(EcoString, usize)> {
902-
match self {
903-
ValueConstructorVariant::LocalVariable {
904-
location: _,
905-
origin:
906-
VariableOrigin {
907-
syntax: _,
908-
declaration:
909-
VariableDeclaration::FunctionParameter {
910-
function_name,
911-
index,
912-
},
913-
},
914-
} => Some((function_name.clone()?, *index)),
915-
916-
ValueConstructorVariant::LocalVariable { .. }
917-
| ValueConstructorVariant::ModuleConstant { .. }
918-
| ValueConstructorVariant::LocalConstant { .. }
919-
| ValueConstructorVariant::ModuleFn { .. }
920-
| ValueConstructorVariant::Record { .. } => None,
921-
}
922-
}
923880
}
924881

925882
#[derive(Debug, Clone, PartialEq, Eq)]

compiler-core/src/type_/environment.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ pub struct VariableUsage {
9999
origin: VariableOrigin,
100100
location: SrcSpan,
101101
usages: usize,
102-
used_recursively: bool,
102+
recursive_usages: usize,
103103
}
104104

105105
impl<'a> Environment<'a> {
@@ -669,7 +669,7 @@ impl Environment<'_> {
669669
origin,
670670
location,
671671
usages: 0,
672-
used_recursively,
672+
recursive_usages,
673673
}) = self
674674
.local_variable_usages
675675
.last_mut()
@@ -680,7 +680,7 @@ impl Environment<'_> {
680680
origin,
681681
location,
682682
usages: 0,
683-
used_recursively: false,
683+
recursive_usages: 0,
684684
},
685685
)
686686
{
@@ -692,7 +692,7 @@ impl Environment<'_> {
692692
origin,
693693
location,
694694
usages: 0,
695-
used_recursively,
695+
recursive_usages,
696696
},
697697
);
698698
self.handle_unused_variables(unused, problems);
@@ -712,16 +712,16 @@ impl Environment<'_> {
712712
}
713713

714714
/// Marks an argument as being passed recursively to a function call.
715-
pub fn set_used_recursively(&mut self, name: &EcoString) {
715+
pub fn increment_recursive_usage(&mut self, name: &EcoString) {
716716
if let Some(VariableUsage {
717-
used_recursively, ..
717+
recursive_usages, ..
718718
}) = self
719719
.local_variable_usages
720720
.iter_mut()
721721
.rev()
722722
.find_map(|scope| scope.get_mut(name))
723723
{
724-
*used_recursively = true;
724+
*recursive_usages += 1;
725725
}
726726
}
727727

@@ -842,7 +842,7 @@ impl Environment<'_> {
842842
origin,
843843
location,
844844
usages,
845-
used_recursively,
845+
recursive_usages,
846846
} in unused.into_values()
847847
{
848848
if usages == 0 {
@@ -851,7 +851,7 @@ impl Environment<'_> {
851851
// If the function parameter is actually used somewhere, but all the
852852
// usages are just passing it along in a recursive call, then it
853853
// counts as being unused too.
854-
else if origin.is_function_parameter() && used_recursively {
854+
else if origin.is_function_parameter() && recursive_usages == usages {
855855
problems.warning(Warning::UnusedRecursiveArgument { location });
856856
}
857857
}

compiler-core/src/type_/expression.rs

Lines changed: 103 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3586,6 +3586,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
35863586
deprecation,
35873587
} = constructor;
35883588

3589+
self.check_recursive_argument_usage(name, &variant, &register_reference);
3590+
35893591
// Emit a warning if the value being used is deprecated.
35903592
if let Deprecation::Deprecated { message } = &deprecation {
35913593
self.problems.warning(Warning::DeprecatedItem {
@@ -3597,20 +3599,22 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
35973599

35983600
self.narrow_implementations(*location, &variant)?;
35993601

3600-
if matches!(
3601-
register_reference,
3602+
match register_reference {
3603+
ReferenceRegistration::DoNotRegisterReferences => (),
3604+
36023605
ReferenceRegistration::RegisterReferences
3603-
) {
3604-
self.register_value_constructor_reference(
3605-
name,
3606-
&variant,
3607-
*location,
3608-
if module.is_some() {
3609-
ReferenceKind::Qualified
3610-
} else {
3611-
ReferenceKind::Unqualified
3612-
},
3613-
);
3606+
| ReferenceRegistration::VariableArgumentReferences { .. } => {
3607+
self.register_value_constructor_reference(
3608+
name,
3609+
&variant,
3610+
*location,
3611+
if module.is_some() {
3612+
ReferenceKind::Qualified
3613+
} else {
3614+
ReferenceKind::Unqualified
3615+
},
3616+
);
3617+
}
36143618
}
36153619

36163620
// Instantiate generic variables into unbound variables for this usage
@@ -3623,6 +3627,43 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
36233627
})
36243628
}
36253629

3630+
fn check_recursive_argument_usage(
3631+
&mut self,
3632+
name: &EcoString,
3633+
variant: &ValueConstructorVariant,
3634+
register_reference: &ReferenceRegistration,
3635+
) {
3636+
// If we are registering references for a call argument
3637+
let ReferenceRegistration::VariableArgumentReferences {
3638+
called_function,
3639+
argument_index,
3640+
} = register_reference
3641+
else {
3642+
return;
3643+
};
3644+
3645+
// If the passed argument is a function's parameter.
3646+
let ValueConstructorVariant::LocalVariable { origin, .. } = variant else {
3647+
return;
3648+
};
3649+
3650+
let VariableDeclaration::FunctionParameter {
3651+
function_name: declaration_function,
3652+
index: declaration_index,
3653+
} = &origin.declaration
3654+
else {
3655+
return;
3656+
};
3657+
3658+
// If the called function is the same where the argument is defined,
3659+
// and the argument is passed unchanged.
3660+
if declaration_function.as_ref() == dbg!(Some(called_function))
3661+
&& declaration_index == argument_index
3662+
{
3663+
self.environment.increment_recursive_usage(name);
3664+
}
3665+
}
3666+
36263667
fn register_value_constructor_reference(
36273668
&mut self,
36283669
referenced_name: &EcoString,
@@ -4423,14 +4464,14 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
44234464

44244465
fn infer_call_argument(
44254466
&mut self,
4426-
fun: &TypedExpr,
4427-
value: UntypedExpr,
4467+
called_function: &TypedExpr,
4468+
argument: UntypedExpr,
44284469
argument_index: usize,
44294470
type_: Arc<Type>,
44304471
kind: ArgumentKind,
44314472
) -> TypedExpr {
44324473
let type_ = collapse_links(type_);
4433-
let value = match (&*type_, value) {
4474+
let value = match (&*type_, argument) {
44344475
// If the argument is expected to be a function and we are passed a
44354476
// function literal with the correct number of arguments then we
44364477
// have special handling of this argument, passing in information
@@ -4460,20 +4501,13 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
44604501
location,
44614502
),
44624503

4463-
// Otherwise just perform normal type inference.
4464-
(_, value) => {
4465-
let inferred = self.infer(value);
4466-
4467-
// If the argument is a simple usage of a parameter defined
4468-
// by this same function we want to track it, to report unused
4469-
// parameters that are just passed along the recursive calls.
4470-
if let Some(name) =
4471-
self.argument_used_in_recursive_call(fun, &inferred, argument_index)
4472-
{
4473-
self.environment.set_used_recursively(&name);
4474-
}
4475-
inferred
4504+
// If the argument is a regular var then we specialise.
4505+
(_, UntypedExpr::Var { location, name }) => {
4506+
self.infer_variable_call_arg(called_function, name, location, argument_index)
44764507
}
4508+
4509+
// Otherwise just perform normal type inference.
4510+
(_, argument) => self.infer(argument),
44774511
};
44784512

44794513
if let Err(error) = unify(type_.clone(), value.type_()) {
@@ -4484,39 +4518,41 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
44844518
value
44854519
}
44864520

4487-
/// Given a function being called and one of its arguments, this returns the
4488-
/// argument's definition location if it was defined by that same function.
4489-
/// For example:
4490-
///
4491-
/// ```gleam
4492-
/// fn wibble(x) {
4493-
/// // ^ Defined by the wibble function
4494-
/// wibble(x, 1)
4495-
/// // ^ And used in the recursive call unchanged!
4496-
/// }
4497-
/// ```
4498-
///
4499-
fn argument_used_in_recursive_call(
4500-
&self,
4521+
fn infer_variable_call_arg(
4522+
&mut self,
45014523
called_function: &TypedExpr,
4502-
argument: &TypedExpr,
4524+
argument_name: EcoString,
4525+
argument_location: SrcSpan,
45034526
argument_index: usize,
4504-
) -> Option<EcoString> {
4505-
let (argument, name) = argument.var_constructor()?;
4506-
let (function_defining_argument, index_in_argument_list) =
4507-
argument.variant.argument_function_and_index()?;
4508-
4509-
let (module, function) = called_function
4510-
.var_constructor()
4511-
.and_then(|(constructor, _)| constructor.variant.function_module_and_name())?;
4512-
4513-
if self.environment.current_module == module
4514-
&& function == function_defining_argument
4515-
&& argument_index == index_in_argument_list
4527+
) -> TypedExpr {
4528+
// If the called function is a function defined in this same module we
4529+
// pass it along to the `infer_var` function so that we can check if the
4530+
// argument is being passed recursively to the function that is defining
4531+
// it.
4532+
let references = if let TypedExpr::Var {
4533+
constructor:
4534+
ValueConstructor {
4535+
variant: ValueConstructorVariant::ModuleFn { name, module, .. },
4536+
..
4537+
},
4538+
..
4539+
} = called_function
4540+
&& *module == self.environment.current_module
45164541
{
4517-
Some(name.clone())
4542+
ReferenceRegistration::VariableArgumentReferences {
4543+
called_function: name.clone(),
4544+
argument_index,
4545+
}
45184546
} else {
4519-
None
4547+
ReferenceRegistration::RegisterReferences
4548+
};
4549+
4550+
match self.infer_var(argument_name, argument_location, references) {
4551+
Ok(result) => result,
4552+
Err(error) => {
4553+
self.problems.error(error);
4554+
self.error_expr(argument_location)
4555+
}
45204556
}
45214557
}
45224558

@@ -4785,10 +4821,18 @@ fn is_trusted_pure_module(environment: &Environment<'_>) -> bool {
47854821
environment.origin == Origin::Src
47864822
}
47874823

4788-
#[derive(Debug, Clone, Copy)]
4824+
#[derive(Debug, Clone)]
47894825
enum ReferenceRegistration {
47904826
RegisterReferences,
47914827
DoNotRegisterReferences,
4828+
4829+
/// If the variable is being passed to a function that is defined in the
4830+
/// current module, then this will have the name of the function and the
4831+
/// argument of the variable being passed as an argument
4832+
VariableArgumentReferences {
4833+
called_function: EcoString,
4834+
argument_index: usize,
4835+
},
47924836
}
47934837

47944838
fn extract_typed_use_call_assignments(

compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__unused_recursive_function_argument.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ warning: Unused function argument
1414
┌─ /src/warning/wrn.gleam:2:13
1515
1616
2 │ pub fn main(x: Int) {
17-
^ This argument is unused
17+
^ This argument is never used
1818

1919
This argument is passed to the function when recursing, but it's never used
2020
for anything.

compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__unused_recursive_function_argument_2.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ warning: Unused function argument
1717
┌─ /src/warning/wrn.gleam:2:13
1818
1919
2 │ pub fn main(x, times) {
20-
^ This argument is unused
20+
^ This argument is never used
2121

2222
This argument is passed to the function when recursing, but it's never used
2323
for anything.

compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__unused_recursive_function_inside_anonymous_function.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ warning: Unused function argument
1515
┌─ /src/warning/wrn.gleam:2:13
1616
1717
2 │ pub fn main(x: Int) {
18-
^ This argument is unused
18+
^ This argument is never used
1919

2020
This argument is passed to the function when recursing, but it's never used
2121
for anything.

compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__unused_recursive_function_with_shadowing.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ warning: Unused function argument
1515
┌─ /src/warning/wrn.gleam:2:13
1616
1717
2 │ pub fn main(x: Int) {
18-
^ This argument is unused
18+
^ This argument is never used
1919

2020
This argument is passed to the function when recursing, but it's never used
2121
for anything.

compiler-core/src/warning.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ but it's never used for anything.",
731731
src: src.clone(),
732732
path: path.to_path_buf(),
733733
label: diagnostic::Label {
734-
text: Some("This argument is unused".into()),
734+
text: Some("This argument is never used".into()),
735735
span: *location,
736736
},
737737
extra_labels: vec![],

0 commit comments

Comments
 (0)