Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions gitgalaxy/core/detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -4835,6 +4835,51 @@ def _count_top_level_args(self, args_str: str, treat_as_body: bool = False) -> i
if not body.strip():
return 0

# #2309: Dart's named (`{...}`) and optional-positional (`[...]`) parameter
# groups are always the TERMINAL element of a parameter list -- `({a, b})`,
# `([a, b])`, `(pos, {a, b})` -- and the commas inside them are real argument
# separators. `{`/`[` otherwise read as nesting in the loop below, hiding
# every one of those commas (`EditableText({super.key, required this.x, ...})`
# measured 1 arg instead of ~40). Neutralise just that one terminal group's
# own delimiters so its members land at the top level. A `{...}`/`[...]` that
# is a default-value literal (`{int x = const [1, 2]}`) is never flush against
# the body's end, so it stays nested and its commas stay (correctly) ignored.
if self.primary_lang_id == "dart":
stripped_end = len(body.rstrip())
if stripped_end and body[stripped_end - 1] in "}]":
gdepth = 0
g_instr = False
g_quote = ""
last_group_open = -1
gi = 0
while gi < stripped_end:
gch = body[gi]
if g_instr:
if gch == "\\":
gi += 2
continue
if gch == g_quote:
g_instr = False
elif gch in ("'", '"', "`"):
g_instr = True
g_quote = gch
elif gch in "([{":
if gch in "[{" and gdepth == 0:
last_group_open = gi
gdepth += 1
elif gch in ")]}":
gdepth = max(0, gdepth - 1)
gi += 1
if last_group_open != -1 and gdepth == 0:
# the terminal group closes at stripped_end-1; blank both delimiters
body = (
body[:last_group_open]
+ " "
+ body[last_group_open + 1 : stripped_end - 1]
+ " "
+ body[stripped_end:]
)

depth = 0
in_string = False
quote_char = ""
Expand Down
2 changes: 1 addition & 1 deletion gitgalaxy/standards/language_standards.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
| Cpp | 87.0% | 95.0% | 100.0% | 100.0% |
| Csharp | 100.0% | 100.0% | 100.0% | 100.0% |
| Css | 100.0% | 100.0% | N/A | N/A |
| Dart | 99.4% | 99.3% | 100.0% | 100.0% |
| Dart | 99.5% | 99.3% | 100.0% | 100.0% |
| Fortran | 98.6% | 100.0% | 100.0% | 100.0% |
| Go | 100.0% | 100.0% | 100.0% | 100.0% |
| Groovy | N/A | N/A | N/A | N/A |
Expand Down
23 changes: 23 additions & 0 deletions tests/core_engine/test_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -2494,6 +2494,29 @@ def test_count_top_level_args_angle_brackets_still_track_generics_for_generic_la
assert cpp_detector._count_top_level_args("(std::vector<int> a, int b)") == 2, "cpp template arg regressed"


def test_count_top_level_args_dart_named_and_optional_parameter_groups_2309():
"""
#2309: Dart's named (`{...}`) and optional-positional (`[...]`) parameter
groups are the terminal element of a parameter list, and the commas inside
them are real argument separators. Before the fix, the group's own `{`/`[`
read as nesting and hid every one -- a Flutter widget constructor like
`EditableText({super.key, required this.controller, ...})` counted 1 arg
instead of ~40. A `{...}`/`[...]` that is a default-VALUE literal instead
(never flush against the parameter list's end) must still stay nested.
"""
d = StructuralExtractor("dart", {"dart": {"rules": {}}})
assert d._count_top_level_args("({super.key, required this.controller, this.readOnly})") == 3
assert d._count_top_level_args("({super.parent})") == 1
assert d._count_top_level_args("(this.a, this.b, this.c)") == 3
assert d._count_top_level_args("(int a, int b, {required Key k, bool flag = false})") == 4
assert d._count_top_level_args("([int a = 1, int b = 2])") == 2
assert d._count_top_level_args("({this.a, this.b,})") == 2 # trailing comma
assert d._count_top_level_args("()") == 0
# default-value literals must NOT leak their internal commas:
assert d._count_top_level_args("({List<int> x = const [1, 2, 3]})") == 1
assert d._count_top_level_args("({Map<String, int> m = const {'a': 1, 'b': 2}})") == 1


def test_count_top_level_args_function_pointer_return_type_1854():
"""
#1854: a C/C++ function returning a function pointer wraps its own name
Expand Down
Loading
Loading