Determine if a line is a function declaration/definition (not a call).
(code: str)
| 151 | |
| 152 | |
| 153 | def _is_function_declaration(code: str) -> bool: |
| 154 | """Determine if a line is a function declaration/definition (not a call).""" |
| 155 | match = _FUNC_DECL_PATTERN.match(code) |
| 156 | if not match: |
| 157 | return False |
| 158 | |
| 159 | prefix = match.group(1).strip() |
| 160 | func_name = match.group(2) |
| 161 | base_name = func_name.rsplit("::", 1)[-1] if "::" in func_name else func_name |
| 162 | |
| 163 | # Skip keywords, destructors, operators, macros, typedefs |
| 164 | if base_name in _STATEMENT_KEYWORDS: |
| 165 | return False |
| 166 | if base_name.startswith("~"): |
| 167 | return False |
| 168 | if _OPERATOR_PATTERN.search(code): |
| 169 | return False |
| 170 | if code.startswith("typedef "): |
| 171 | return False |
| 172 | # ALL_CAPS = macro call (FL_WARN, ESP_ERROR_CHECK, etc.) |
| 173 | if re.match(r"^[A-Z][A-Z0-9_]*$", base_name): |
| 174 | return False |
| 175 | # asm/volatile inline assembly |
| 176 | if base_name in ("asm", "volatile", "__volatile__", "__asm__"): |
| 177 | return False |
| 178 | # Initializer list continuation: , mFoo(...) { |
| 179 | if code.startswith(","): |
| 180 | return False |
| 181 | # Lambda expressions: fl::thread t([captures]() { |
| 182 | if "[" in code: |
| 183 | return False |
| 184 | # C API calls: NVIC_EnableIRQ(...), DuplicateHandle(...) — uppercase name |
| 185 | # with no return type prefix is ambiguous with constructors, but names |
| 186 | # containing underscores with an ALL_CAPS prefix are C function calls |
| 187 | if "_" in base_name and re.match(r"^[A-Z][A-Z0-9]*_", base_name): |
| 188 | return False |
| 189 | |
| 190 | # Has a return type prefix → declaration |
| 191 | if prefix: |
| 192 | tokens = prefix.split() |
| 193 | for token in tokens: |
| 194 | if token in _STATEMENT_KEYWORDS: |
| 195 | return False |
| 196 | for token in tokens: |
| 197 | clean = token.rstrip("*&").lstrip("*&") |
| 198 | if "<" in clean: |
| 199 | clean = clean[: clean.index("<")] |
| 200 | if not clean: |
| 201 | continue |
| 202 | if clean in _TYPE_KEYWORDS or clean in _QUALIFIERS: |
| 203 | return True |
| 204 | if clean[0].isupper() or "::" in clean: |
| 205 | return True |
| 206 | return True # any prefix token implies return type |
| 207 | return False |
| 208 | |
| 209 | # No prefix + starts uppercase = constructor |
| 210 | if base_name[0].isupper(): |