Skip to content

Commit 07b5c84

Browse files
authored
Fix arity in overloaded function argument error (#883)
When overloaded functions receive an incorrect number of positional arguments, determine which overload has the most similar number of arguments, and then correctly display that number in the error. Closes #520 sass/sass-spec#1496
1 parent 3d1dab3 commit 07b5c84

File tree

3 files changed

+58
-12
lines changed

3 files changed

+58
-12
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## 1.23.5
22

3+
* When an overloaded function receives the wrong number of arguments, guess
4+
which overload the user actually meant to invoke, and display the invalid
5+
argument error for that overload.
6+
37
* When `@error` is used in a function or mixin, print the call site rather than
48
the location of the `@error` itself to better match the behavior of calling a
59
built-in function that throws an error.

lib/src/callable/async_built_in.dart

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,32 @@ class AsyncBuiltInCallable implements AsyncCallable {
5656
/// Returns the argument declaration and Dart callback for the given
5757
/// positional and named arguments.
5858
///
59-
/// Note that this doesn't guarantee that [positional] and [names] are valid
60-
/// for the returned [ArgumentDeclaration].
59+
/// If no exact match is found, finds the closest approximation. Note that this
60+
/// doesn't guarantee that [positional] and [names] are valid for the returned
61+
/// [ArgumentDeclaration].
6162
Tuple2<ArgumentDeclaration, _Callback> callbackFor(
62-
int positional, Set<String> names) =>
63-
_overloads.take(_overloads.length - 1).firstWhere(
64-
(overload) => overload.item1.matches(positional, names),
65-
orElse: () => _overloads.last);
63+
int positional, Set<String> names) {
64+
Tuple2<ArgumentDeclaration, _Callback> fuzzyMatch;
65+
int minMismatchDistance;
66+
67+
for (var overload in _overloads) {
68+
// Ideally, find an exact match.
69+
if (overload.item1.matches(positional, names)) return overload;
70+
71+
var mismatchDistance = overload.item1.arguments.length - positional;
72+
73+
if (minMismatchDistance != null) {
74+
if (mismatchDistance.abs() > minMismatchDistance.abs()) continue;
75+
// If two overloads have the same mismatch distance, favor the overload
76+
// that has more arguments.
77+
if (mismatchDistance.abs() == minMismatchDistance.abs() &&
78+
mismatchDistance < 0) continue;
79+
}
80+
81+
minMismatchDistance = mismatchDistance;
82+
fuzzyMatch = overload;
83+
}
84+
85+
return fuzzyMatch;
86+
}
6687
}

lib/src/callable/built_in.dart

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,34 @@ class BuiltInCallable implements Callable, AsyncBuiltInCallable {
5555
/// Returns the argument declaration and Dart callback for the given
5656
/// positional and named arguments.
5757
///
58-
/// Note that this doesn't guarantee that [positional] and [names] are valid
59-
/// for the returned [ArgumentDeclaration].
58+
/// If no exact match is found, finds the closest approximation. Note that this
59+
/// doesn't guarantee that [positional] and [names] are valid for the returned
60+
/// [ArgumentDeclaration].
6061
Tuple2<ArgumentDeclaration, _Callback> callbackFor(
61-
int positional, Set<String> names) =>
62-
_overloads.take(_overloads.length - 1).firstWhere(
63-
(overload) => overload.item1.matches(positional, names),
64-
orElse: () => _overloads.last);
62+
int positional, Set<String> names) {
63+
Tuple2<ArgumentDeclaration, _Callback> fuzzyMatch;
64+
int minMismatchDistance;
65+
66+
for (var overload in _overloads) {
67+
// Ideally, find an exact match.
68+
if (overload.item1.matches(positional, names)) return overload;
69+
70+
var mismatchDistance = overload.item1.arguments.length - positional;
71+
72+
if (minMismatchDistance != null) {
73+
if (mismatchDistance.abs() > minMismatchDistance.abs()) continue;
74+
// If two overloads have the same mismatch distance, favor the overload
75+
// that has more arguments.
76+
if (mismatchDistance.abs() == minMismatchDistance.abs() &&
77+
mismatchDistance < 0) continue;
78+
}
79+
80+
minMismatchDistance = mismatchDistance;
81+
fuzzyMatch = overload;
82+
}
83+
84+
return fuzzyMatch;
85+
}
6586

6687
/// Returns a copy of this callable with the given [name].
6788
BuiltInCallable withName(String name) =>

0 commit comments

Comments
 (0)