Apply safe fixes to files with violations. Args: violations_dict: Dictionary of violations from checker file_contents: Dictionary mapping file paths to their contents Returns: Tuple of (files_fixed, violations_fixed)
(violations_dict: dict, file_contents: dict)
| 482 | |
| 483 | |
| 484 | def apply_fixes(violations_dict: dict, file_contents: dict) -> tuple[int, int]: |
| 485 | """Apply safe fixes to files with violations. |
| 486 | |
| 487 | Args: |
| 488 | violations_dict: Dictionary of violations from checker |
| 489 | file_contents: Dictionary mapping file paths to their contents |
| 490 | |
| 491 | Returns: |
| 492 | Tuple of (files_fixed, violations_fixed) |
| 493 | """ |
| 494 | from pathlib import Path |
| 495 | |
| 496 | files_fixed = 0 |
| 497 | violations_fixed = 0 |
| 498 | |
| 499 | for file_path, violations in violations_dict.items(): |
| 500 | if file_path not in file_contents: |
| 501 | continue |
| 502 | |
| 503 | lines = file_contents[file_path].lines.copy() |
| 504 | fixed_any = False |
| 505 | |
| 506 | # Process violations in reverse line order to avoid line number shifts |
| 507 | for line_num, msg in sorted(violations, reverse=True, key=lambda x: x[0]): |
| 508 | # Extract the offending include path |
| 509 | match_include = re.search(r'#include "([^"]+)"', msg) |
| 510 | if not match_include: |
| 511 | continue |
| 512 | |
| 513 | include_path = match_include.group(1) |
| 514 | corrected_path = None |
| 515 | use_angle_brackets = False |
| 516 | |
| 517 | # Try to infer the correct path |
| 518 | banned_replacement = get_banned_subpath_replacement(include_path) |
| 519 | if banned_replacement is not None: |
| 520 | corrected_path = banned_replacement |
| 521 | elif is_known_sdk_header(include_path): |
| 522 | # Convert to angle bracket syntax - check this first! |
| 523 | corrected_path = Path(include_path).name |
| 524 | use_angle_brackets = True |
| 525 | elif is_fastled_platform_relative(include_path): |
| 526 | # Bare platform name needs "platforms/" prefix |
| 527 | corrected_path = f"platforms/{include_path}" |
| 528 | elif is_relative_path(include_path): |
| 529 | # Relative paths are always wrong |
| 530 | continue # Skip, need manual review |
| 531 | elif typo_suggestion := get_typo_suggestion(include_path): |
| 532 | # Apply typo correction |
| 533 | typo_prefix = include_path.split("/")[0] + "/" |
| 534 | rest_of_path = include_path[len(typo_prefix) :] |
| 535 | corrected_path = typo_suggestion + rest_of_path |
| 536 | elif "Header not found" in msg: |
| 537 | # Try to find the file in src/ - but only fix if exactly one match |
| 538 | from ci.util.paths import PROJECT_ROOT |
| 539 | |
| 540 | basename = Path(include_path).name |
| 541 | # Search for the file in src/ |
no test coverage detected