Skip to content

Commit 5850073

Browse files
authored
Merge pull request #11814 from lucasssvaz/fix/ltoa
fix(ltoa): Use proper `labs` for long values and fix affected sketches detection
2 parents 4ce23ce + cddf76f commit 5850073

File tree

2 files changed

+120
-4
lines changed

2 files changed

+120
-4
lines changed

.github/scripts/get_affected.py

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,123 @@ def detect_universal_ctags() -> bool:
222222
except Exception:
223223
return False
224224

225+
def normalize_function_signature(signature: str) -> str:
226+
"""
227+
Normalize a function signature by removing parameter names, keeping only types.
228+
229+
This handles cases where header declarations and implementations have different parameter names.
230+
Uses a simple heuristic: the last word in each parameter is typically the parameter name.
231+
232+
For example:
233+
- "ltoa(long val, char *s, int radix)" -> "ltoa(long,char *,int)"
234+
- "ltoa(long value, char *result, int base)" -> "ltoa(long,char *,int)"
235+
236+
Args:
237+
signature: The function signature string, e.g., "(long val, char *s, int radix)"
238+
239+
Returns:
240+
Normalized signature with parameter names removed, e.g., "(long,char *,int)"
241+
"""
242+
if not signature:
243+
return signature
244+
245+
# Normalize signatures: treat (void) and () as equivalent (both mean no parameters)
246+
if signature == "(void)":
247+
return "()"
248+
249+
if not (signature.startswith("(") and signature.endswith(")")):
250+
return signature
251+
252+
# Handle const qualifier at the end (e.g., "(int i) const")
253+
const_qualifier = ""
254+
if signature.endswith(" const"):
255+
signature = signature[:-6] # Remove " const"
256+
const_qualifier = " const"
257+
258+
# Extract parameter list without parentheses
259+
param_list = signature[1:-1]
260+
if not param_list.strip():
261+
return "()" + const_qualifier
262+
263+
# Split by comma and process each parameter
264+
params = []
265+
for param in param_list.split(","):
266+
param = param.strip()
267+
if not param:
268+
continue
269+
270+
# Handle default parameters (e.g., "int x = 5")
271+
if "=" in param:
272+
param = param.split("=")[0].strip()
273+
274+
# Try simple approach first: remove the last word
275+
# This works for most cases: "int x" -> "int", "MyStruct s" -> "MyStruct"
276+
import re
277+
278+
# Handle arrays first: "int arr[10]" -> "int [10]", "char *argv[]" -> "char *[]"
279+
array_match = re.match(r'^(.+?)\s+(\w+)((?:\[\d*\])+)$', param)
280+
if array_match:
281+
type_part = array_match.group(1).strip()
282+
array_brackets = array_match.group(3)
283+
params.append(type_part + array_brackets)
284+
else:
285+
# Handle function pointers: "int (*func)(int, char)" -> "int (*)(int, char)"
286+
func_ptr_match = re.match(r'^(.+?)\s*\(\s*\*\s*(\w+)\s*\)\s*\((.+?)\)\s*$', param)
287+
if func_ptr_match:
288+
return_type = func_ptr_match.group(1).strip()
289+
inner_params = func_ptr_match.group(3).strip()
290+
# Recursively normalize the inner parameters
291+
if inner_params:
292+
inner_normalized = normalize_function_signature(f"({inner_params})")
293+
inner_normalized = inner_normalized[1:-1] # Remove outer parentheses
294+
else:
295+
inner_normalized = ""
296+
params.append(f"{return_type} (*)({inner_normalized})")
297+
else:
298+
# Try simple approach: remove the last word
299+
simple_match = re.match(r'^(.+)\s+(\w+)$', param)
300+
if simple_match:
301+
# Simple case worked - just remove the last word
302+
type_part = simple_match.group(1).strip()
303+
params.append(type_part)
304+
else:
305+
# Fallback to complex regex for edge cases with pointers
306+
# First, try to match cases with pointers/references (including multiple *)
307+
# Pattern: (everything before) (one or more * or &) (space) (parameter name)
308+
m = re.match(r'^(.+?)(\s*[*&]+\s+)(\w+)$', param)
309+
if m:
310+
# Keep everything before the pointers, plus the pointers (without the space before param name)
311+
type_part = m.group(1) + m.group(2).rstrip()
312+
params.append(type_part.strip())
313+
else:
314+
# Try to match cases without space between type and pointer: "char*ptr", "char**ptr"
315+
m = re.match(r'^(.+?)([*&]+)(\w+)$', param)
316+
if m:
317+
# Keep everything before the pointers, plus the pointers
318+
type_part = m.group(1) + m.group(2)
319+
params.append(type_part.strip())
320+
else:
321+
# Single word - assume it's a type
322+
params.append(param.strip())
323+
324+
# Normalize spacing around pointers to ensure consistent output
325+
# This ensures "char *" and "char*" both become "char *"
326+
if params:
327+
last_param = params[-1]
328+
# Normalize spacing around * and & symbols
329+
# Replace multiple spaces with single space, ensure space before * and &
330+
normalized = re.sub(r'\s+', ' ', last_param) # Collapse multiple spaces
331+
normalized = re.sub(r'\s*([*&]+)', r' \1', normalized) # Ensure space before * and &
332+
normalized = re.sub(r'([*&]+)\s*', r'\1 ', normalized) # Ensure space after * and &
333+
normalized = re.sub(r'\s+', ' ', normalized).strip() # Clean up extra spaces
334+
params[-1] = normalized
335+
336+
result = "(" + ",".join(params) + ")"
337+
if const_qualifier:
338+
result += const_qualifier
339+
340+
return result
341+
225342
def build_qname_from_tag(tag: dict) -> str:
226343
"""
227344
Compose a qualified name for a function/method using scope + name + signature.
@@ -231,9 +348,8 @@ def build_qname_from_tag(tag: dict) -> str:
231348
name = tag.get("name") or ""
232349
signature = tag.get("signature") or ""
233350

234-
# Normalize signatures: treat (void) and () as equivalent (both mean no parameters)
235-
if signature == "(void)":
236-
signature = "()"
351+
# Normalize the signature to remove parameter names
352+
signature = normalize_function_signature(signature)
237353

238354
qparts = []
239355
if scope:

cores/esp32/stdlib_noniso.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ char *ltoa(long value, char *result, int base) {
4949
}
5050

5151
char *out = result;
52-
long quotient = abs(value);
52+
long quotient = labs(value);
5353

5454
do {
5555
const long tmp = quotient / base;

0 commit comments

Comments
 (0)