Bans bare C libm calls inside src/ (PR #3012 / issue #3002). Forces new code to go through `fl::sqrt(x)`, `fl::atan2(y, x)`, etc., which on Low-memory targets route to hand-rolled polynomial / Newton-Raphson approximations rather than dragging libm + libgcc soft-FP into the link.
| 149 | |
| 150 | |
| 151 | class BareLibmChecker(FileContentChecker): |
| 152 | """Bans bare C libm calls inside src/ (PR #3012 / issue #3002). |
| 153 | |
| 154 | Forces new code to go through `fl::sqrt(x)`, `fl::atan2(y, x)`, etc., |
| 155 | which on Low-memory targets route to hand-rolled polynomial / |
| 156 | Newton-Raphson approximations rather than dragging libm + libgcc |
| 157 | soft-FP into the link. |
| 158 | """ |
| 159 | |
| 160 | def __init__(self): |
| 161 | self.violations: dict[str, list[tuple[int, str]]] = {} |
| 162 | |
| 163 | def should_process_file(self, file_path: str) -> bool: |
| 164 | if not file_path.startswith(str(SRC_ROOT)): |
| 165 | return False |
| 166 | if not file_path.endswith((".cpp", ".h", ".hpp")): |
| 167 | return False |
| 168 | norm = file_path.replace("\\", "/") |
| 169 | # The fl::math compat shim is exempt — it IS the wrapper. |
| 170 | for exempt in EXEMPT_FILES: |
| 171 | if norm.endswith(exempt): |
| 172 | return False |
| 173 | # Third-party upstream code (stb_vorbis, cq_kernel, etc.) is not |
| 174 | # under our authorship; the checker would generate noise. |
| 175 | if "/third_party/" in norm: |
| 176 | return False |
| 177 | # Subsystems still on the migration list (see EXEMPT_PREFIXES). New |
| 178 | # code in these directories should still prefer fl::, but the |
| 179 | # checker stays silent for now so this PR doesn't block on a 34-file |
| 180 | # refactor. |
| 181 | for prefix in EXEMPT_PREFIXES: |
| 182 | # Match `src/<prefix>` anywhere in the normalized path. |
| 183 | if "/src/" + prefix in norm or norm.startswith(prefix): |
| 184 | return False |
| 185 | return True |
| 186 | |
| 187 | def check_file_content(self, file_content: FileContent) -> list[str]: |
| 188 | violations: list[tuple[int, str]] = [] |
| 189 | in_block_comment = False |
| 190 | |
| 191 | for line_number, line in enumerate(file_content.lines, 1): |
| 192 | stripped = line.strip() |
| 193 | |
| 194 | # Track /* ... */ blocks. Single-line `/* ... */` is handled |
| 195 | # by the trailing-comment strip below. |
| 196 | if in_block_comment: |
| 197 | if "*/" in line: |
| 198 | in_block_comment = False |
| 199 | continue |
| 200 | if "/*" in line and "*/" not in line: |
| 201 | in_block_comment = True |
| 202 | continue |
| 203 | |
| 204 | if stripped.startswith("//"): |
| 205 | continue |
| 206 | if _SUPPRESS in line: |
| 207 | continue |
| 208 |
no outgoing calls
no test coverage detected