Check header files for #include "FastLED.h" - internal headers should use fl/system/fastled.h Returns: List of (line_number, line_content) tuples for offending includes
(file_path: Path)
| 55 | |
| 56 | |
| 57 | def check_fastled_header_usage(file_path: Path) -> List[Tuple[int, str]]: |
| 58 | """ |
| 59 | Check header files for #include "FastLED.h" - internal headers should use fl/system/fastled.h |
| 60 | |
| 61 | Returns: |
| 62 | List of (line_number, line_content) tuples for offending includes |
| 63 | """ |
| 64 | violations: List[Tuple[int, str]] = [] |
| 65 | |
| 66 | # Only check header files |
| 67 | if file_path.suffix not in {'.h', '.hpp'}: |
| 68 | return violations |
| 69 | |
| 70 | # Skip FastLED.h itself and fastspi.h (they're public headers) |
| 71 | if file_path.name in {'FastLED.h', 'fastspi.h'}: |
| 72 | return violations |
| 73 | |
| 74 | # Skip example files |
| 75 | if 'examples' in file_path.parts: |
| 76 | return violations |
| 77 | |
| 78 | # Only check files in src/ directory |
| 79 | if 'src' not in file_path.parts: |
| 80 | return violations |
| 81 | |
| 82 | # Regex to match #include "FastLED.h" or #include <FastLED.h> |
| 83 | # This should match the root header, not fl/system/fastled.h |
| 84 | include_pattern = re.compile(r'^\s*#\s*include\s+[<"]FastLED\.h[>"]') |
| 85 | |
| 86 | # Allow exemptions via "// ok include" comment on the same line |
| 87 | exemption_pattern = re.compile(r'//\s*ok\s+include', re.IGNORECASE) |
| 88 | |
| 89 | # Pattern to detect #define FASTLED_INTERNAL |
| 90 | fastled_internal_pattern = re.compile(r'^\s*#\s*define\s+FASTLED_INTERNAL') |
| 91 | |
| 92 | try: |
| 93 | with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: |
| 94 | lines = f.readlines() |
| 95 | |
| 96 | for line_num, line in enumerate(lines, start=1): |
| 97 | match = include_pattern.match(line) |
| 98 | if match: |
| 99 | # Check if the line has an exemption comment |
| 100 | if exemption_pattern.search(line): |
| 101 | continue |
| 102 | |
| 103 | # Check if FASTLED_INTERNAL was defined within the previous 5 lines |
| 104 | # This allows for legitimate uses with #define FASTLED_INTERNAL before #include |
| 105 | has_internal_define = False |
| 106 | lookback = min(5, line_num - 1) |
| 107 | for i in range(1, lookback + 1): |
| 108 | prev_line = lines[line_num - i - 1] |
| 109 | if fastled_internal_pattern.match(prev_line): |
| 110 | has_internal_define = True |
| 111 | break |
| 112 | |
| 113 | if not has_internal_define: |
| 114 | violations.append((line_num, line.rstrip())) |