Skip to content

Commit 769cefc

Browse files
feat(cpp): dead-code hardening — reachability, never-flag, contracts, dynamic markers (#284)
* feat(dead-code): never-flag conventions for C and C++ apps, demos, tests, generated, vendored trees * feat(dead-code): C and C++ contract methods (ctor, dtor, operator overloads, STL CPOs, coroutine machinery) * feat(dead-code): C and C++ dynamic-import markers for dlopen, Qt MOC, GoogleTest, libFuzzer, registration macros * feat(dead-code): C and C++ reachability hook with header-by-symbol, main-carrier, sibling, and conditional-alt rescue
1 parent c984f94 commit 769cefc

6 files changed

Lines changed: 1314 additions & 0 deletions

File tree

packages/core/src/repowise/core/analysis/dead_code/analyzer.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
_is_fixture_path,
2929
)
3030
from .contract_methods import is_contract_method
31+
from .cpp_reachability import (
32+
build_cpp_package_files,
33+
is_cpp_file_reachable,
34+
is_cpp_path,
35+
)
3136
from .go_reachability import build_go_package_files, is_go_file_reachable
3237
from .jvm_reachability import build_jvm_package_files, is_jvm_file_reachable
3338

@@ -150,6 +155,11 @@ def _non_importable_kinds(language: str) -> frozenset[str]:
150155
"WinMain", # ANSI WinMain
151156
"wmain", # Unicode console main
152157
"ServiceMain", # Win32 service entry
158+
# ---- libFuzzer / Honggfuzz / AFL fuzz harness entries ------------
159+
# The fuzzer driver invokes these by name via dlsym; no static
160+
# caller will ever exist.
161+
"LLVMFuzzerTestOneInput",
162+
"LLVMFuzzerInitialize",
153163
# ---- Windows hook / ETW callbacks invoked by macros / runtime ----
154164
"LowLevelKeyboardProc",
155165
"LowLevelMouseProc",
@@ -308,6 +318,8 @@ def __init__(
308318
# Lazily-built JVM (``.java`` + ``.kt``) package-directory map; see
309319
# :mod:`jvm_reachability`.
310320
self._jvm_package_files: dict[str, list[str]] | None = None
321+
# Lazily-built C/C++ directory map; see :mod:`cpp_reachability`.
322+
self._cpp_package_files: dict[str, list[str]] | None = None
311323

312324
def _go_packages(self) -> dict[str, list[str]]:
313325
"""Return the cached Go package map, building it on first use."""
@@ -321,6 +333,12 @@ def _jvm_packages(self) -> dict[str, list[str]]:
321333
self._jvm_package_files = build_jvm_package_files(self.graph)
322334
return self._jvm_package_files
323335

336+
def _cpp_packages(self) -> dict[str, list[str]]:
337+
"""Return the cached C/C++ directory map, building it on first use."""
338+
if self._cpp_package_files is None:
339+
self._cpp_package_files = build_cpp_package_files(self.graph)
340+
return self._cpp_package_files
341+
324342
def analyze(
325343
self,
326344
config: dict | None = None,
@@ -476,6 +494,13 @@ def _detect_unreachable_files(
476494
# files surface as live even with no direct importer.
477495
if is_jvm_file_reachable(node_str, self.graph, self._jvm_packages()):
478496
continue
497+
elif is_cpp_path(node_str):
498+
# C/C++ reachability rescues public-API headers, ``main``-
499+
# bearing TUs (apps/demos/benchmarks/fuzzers), internal
500+
# headers next to their implementation files, and
501+
# conditional-compile alternates that share a stem prefix.
502+
if is_cpp_file_reachable(node_str, self.graph, self._cpp_packages()):
503+
continue
479504
elif self.graph.in_degree(node) > 0:
480505
continue
481506

packages/core/src/repowise/core/analysis/dead_code/constants.py

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,274 @@
171171
# activated by Windows, so it has no static caller.
172172
"*/pch.h",
173173
"*/pch.cpp",
174+
"*/pch.cc",
175+
"*/PrecompiledHeader.cpp",
176+
"*/PrecompiledHeader.cc",
174177
"*/stdafx.h",
175178
"*/stdafx.cpp",
176179
"*ClassFactory.cpp",
177180
"*ClassFactory.h",
181+
# ---- C / C++ conventions ---------------------------------------------
182+
# ``fnmatch`` ``*`` spans ``/`` so a leading ``*`` matches both nested
183+
# module paths and repo-root layouts.
184+
# Apps / demos / examples / tools / benchmarks — every file under these
185+
# trees compiles to a standalone binary by CMake/Bazel ``add_executable``
186+
# / ``cc_binary``. They have no static importer by design.
187+
"*/apps/*.cc",
188+
"*/apps/*.cpp",
189+
"*/apps/*.cxx",
190+
"*/apps/*.c",
191+
"*/apps/*.h",
192+
"*/apps/*.hpp",
193+
"*/apps/**/*.cc",
194+
"*/apps/**/*.cpp",
195+
"*/apps/**/*.cxx",
196+
"*/apps/**/*.c",
197+
"*/apps/**/*.h",
198+
"*/apps/**/*.hpp",
199+
"apps/*.cc",
200+
"apps/*.cpp",
201+
"apps/*.c",
202+
"apps/**/*.cc",
203+
"apps/**/*.cpp",
204+
"apps/**/*.cxx",
205+
"apps/**/*.c",
206+
"apps/**/*.h",
207+
"apps/**/*.hpp",
208+
"*/demos/*.cc",
209+
"*/demos/*.cpp",
210+
"*/demos/*.cxx",
211+
"*/demos/*.c",
212+
"*/demos/**/*.cc",
213+
"*/demos/**/*.cpp",
214+
"*/demos/**/*.cxx",
215+
"*/demos/**/*.c",
216+
"demos/*.cc",
217+
"demos/*.cpp",
218+
"demos/*.c",
219+
"demos/**/*.cc",
220+
"demos/**/*.cpp",
221+
"demos/**/*.cxx",
222+
"demos/**/*.c",
223+
"*/examples/*.cc",
224+
"*/examples/*.cpp",
225+
"*/examples/*.cxx",
226+
"*/examples/*.c",
227+
"*/examples/*.h",
228+
"*/examples/**/*.cc",
229+
"*/examples/**/*.cpp",
230+
"*/examples/**/*.cxx",
231+
"*/examples/**/*.c",
232+
"*/examples/**/*.h",
233+
"*/examples/**/*.hpp",
234+
"examples/*.cc",
235+
"examples/*.cpp",
236+
"examples/**/*.cc",
237+
"examples/**/*.cpp",
238+
"examples/**/*.cxx",
239+
"examples/**/*.c",
240+
"examples/**/*.h",
241+
"*/sample/*.cc",
242+
"*/sample/*.cpp",
243+
"*/samples/*.cc",
244+
"*/samples/*.cpp",
245+
"*/samples/**/*.cc",
246+
"*/samples/**/*.cpp",
247+
"samples/**/*.cc",
248+
"samples/**/*.cpp",
249+
"*/benchmarks/*.cc",
250+
"*/benchmarks/*.cpp",
251+
"*/benchmarks/*.cxx",
252+
"*/benchmarks/*.c",
253+
"*/benchmarks/**/*.cc",
254+
"*/benchmarks/**/*.cpp",
255+
"benchmarks/*.cc",
256+
"benchmarks/*.cpp",
257+
"benchmarks/*.c",
258+
"benchmarks/**/*.cc",
259+
"benchmarks/**/*.cpp",
260+
"*/bench/*.cc",
261+
"*/bench/*.cpp",
262+
"bench/*.cc",
263+
"bench/*.cpp",
264+
# C/C++ tool directories (``leveldbutil.cc`` / ``db_repair.cc`` shape).
265+
"*/tools/*.cc",
266+
"*/tools/*.cpp",
267+
"*/tools/**/*.cc",
268+
"*/tools/**/*.cpp",
269+
"tools/*.cc",
270+
"tools/*.cpp",
271+
"tools/**/*.cc",
272+
"tools/**/*.cpp",
273+
# C/C++ test trees — every framework (GoogleTest / Catch2 / Boost.Test
274+
# / doctest / Google Benchmark / libFuzzer) discovers tests by glob, not
275+
# by static import.
276+
"*/tests/**/*_test.cc",
277+
"*/tests/**/*_test.cpp",
278+
"*/tests/**/*_test.cxx",
279+
"*/tests/**/*_test.c",
280+
"*/tests/**/*_unittest.cc",
281+
"*/tests/**/*_unittest.cpp",
282+
"*/tests/**/*_perftest.cc",
283+
"*/tests/**/*_perftest.cpp",
284+
"*/tests/**/*_perf.cc",
285+
"*/tests/**/*_perf.cpp",
286+
"*/tests/**/*_benchmark.cc",
287+
"*/tests/**/*_benchmark.cpp",
288+
"*/tests/**/*_fuzz.cc",
289+
"*/tests/**/*_fuzz.cpp",
290+
"*/tests/perf/*.cc",
291+
"*/tests/perf/*.cpp",
292+
"*/tests/unit/*.cc",
293+
"*/tests/unit/*.cpp",
294+
"*/tests/unit/*.h",
295+
"*/tests/unit/*.hpp",
296+
"*/tests/fuzz/*.cc",
297+
"*/tests/fuzz/*.cpp",
298+
"*/tests/integration/*.cc",
299+
"*/tests/integration/*.cpp",
300+
"*/tests/manual/*",
301+
"tests/**/*_test.cc",
302+
"tests/**/*_test.cpp",
303+
"tests/**/*_unittest.cc",
304+
"tests/**/*_unittest.cpp",
305+
"tests/**/*_perf.cc",
306+
"tests/**/*_perf.cpp",
307+
"tests/**/*_fuzz.cc",
308+
"tests/**/*_fuzz.cpp",
309+
"tests/perf/*.cc",
310+
"tests/perf/*.cpp",
311+
"tests/unit/*.cc",
312+
"tests/unit/*.cpp",
313+
"tests/unit/*.h",
314+
"tests/fuzz/*.cc",
315+
"tests/fuzz/*.cpp",
316+
"tests/manual/*",
317+
# File-suffix conventions for tests dropped outside a standard test
318+
# directory (GoogleTest / Google Benchmark / libFuzzer / Catch2).
319+
"*_test.cc",
320+
"*_test.cpp",
321+
"*_test.cxx",
322+
"*_test.h",
323+
"*_unittest.cc",
324+
"*_unittest.cpp",
325+
"*_perftest.cc",
326+
"*_perftest.cpp",
327+
"*_perf.cc",
328+
"*_perf.cpp",
329+
"*_benchmark.cc",
330+
"*_benchmark.cpp",
331+
"*_fuzz.cc",
332+
"*_fuzz.cpp",
333+
# Conventional port / example skeleton headers — projects ship them
334+
# to document a portability layer; never actually built.
335+
"*/port_example.h",
336+
"*/port/port_example.h",
337+
"*_example.h",
338+
"*_example.hpp",
339+
"*_example.cc",
340+
# Generated source roots (CMake build dirs, autoconf / out-of-tree
341+
# builds). The walker normally skips them but, when they leak in,
342+
# they're never importers.
343+
"*/build/**",
344+
"build/**",
345+
"*/cmake-build-*/**",
346+
"cmake-build-*/**",
347+
"*/_deps/**",
348+
"_deps/**",
349+
"*/out/build/**",
350+
"*/out/Debug/**",
351+
"*/out/Release/**",
352+
# Generated source-file patterns. Wired in at build time, no static
353+
# importer; the analyzer should silence them universally.
354+
"moc_*.cpp", # Qt MOC
355+
"moc_*.cc",
356+
"ui_*.h", # Qt UIC
357+
"qrc_*.cpp", # Qt RCC
358+
"qrc_*.cc",
359+
"*.moc", # inline MOC includes
360+
"*.pb.cc", # protoc generated
361+
"*.pb.h",
362+
"*.pb-c.c", # protobuf-c
363+
"*.pb-c.h",
364+
"*.grpc.pb.cc", # protoc-gen-grpc
365+
"*.grpc.pb.h",
366+
"*.capnp.c++", # Cap'n Proto
367+
"*.capnp.h",
368+
"*.flatbuffers.h",
369+
"*_generated.h", # FlatBuffers convention
370+
"*.tab.c", # Bison / Yacc
371+
"*.tab.h",
372+
"*.yy.c", # Flex / Lex
373+
"*_lex.cc",
374+
"*_wrap.cxx", # SWIG
375+
"*_wrap.cpp",
376+
"*.cython.cpp", # Cython
377+
# Vendored / third-party roots. The existing ``vendor`` / ``third_party``
378+
# / ``deps`` globs only cover ``.c`` / ``.h``; extend them to the full
379+
# C++ extension set, and add the additional vendor conventions
380+
# ``external/`` / ``extern/`` / ``contrib/`` / ``submodules/``.
381+
"*/vendor/**/*.cc",
382+
"*/vendor/**/*.cpp",
383+
"*/vendor/**/*.cxx",
384+
"*/vendor/**/*.hpp",
385+
"*/vendor/**/*.hxx",
386+
"vendor/**/*.cc",
387+
"vendor/**/*.cpp",
388+
"vendor/**/*.cxx",
389+
"vendor/**/*.hpp",
390+
"*/third_party/**/*.cc",
391+
"*/third_party/**/*.cpp",
392+
"*/third_party/**/*.cxx",
393+
"*/third_party/**/*.hpp",
394+
"*/third_party/**/*.hxx",
395+
"third_party/**/*.cc",
396+
"third_party/**/*.cpp",
397+
"third_party/**/*.cxx",
398+
"third_party/**/*.hpp",
399+
"*/deps/**/*.cc",
400+
"*/deps/**/*.cpp",
401+
"*/deps/**/*.cxx",
402+
"*/deps/**/*.hpp",
403+
"deps/**/*.cc",
404+
"deps/**/*.cpp",
405+
"deps/**/*.cxx",
406+
"deps/**/*.hpp",
407+
"*/external/**/*.c",
408+
"*/external/**/*.h",
409+
"*/external/**/*.cc",
410+
"*/external/**/*.cpp",
411+
"*/external/**/*.cxx",
412+
"*/external/**/*.hpp",
413+
"*/external/**/*.hxx",
414+
"external/**/*.c",
415+
"external/**/*.h",
416+
"external/**/*.cc",
417+
"external/**/*.cpp",
418+
"external/**/*.cxx",
419+
"external/**/*.hpp",
420+
"*/extern/**/*.c",
421+
"*/extern/**/*.h",
422+
"*/extern/**/*.cc",
423+
"*/extern/**/*.cpp",
424+
"*/extern/**/*.hpp",
425+
"extern/**/*.c",
426+
"extern/**/*.h",
427+
"extern/**/*.cc",
428+
"extern/**/*.cpp",
429+
"*/contrib/**/*.c",
430+
"*/contrib/**/*.h",
431+
"*/contrib/**/*.cc",
432+
"*/contrib/**/*.cpp",
433+
"*/contrib/**/*.cxx",
434+
"*/contrib/**/*.hpp",
435+
"contrib/**/*.c",
436+
"contrib/**/*.h",
437+
"contrib/**/*.cc",
438+
"contrib/**/*.cpp",
439+
"*/submodules/**",
440+
"submodules/**",
441+
"*/.deps/**",
178442
# ---- Rust / Cargo conventions ----------------------------------------
179443
# Build scripts (executed by Cargo at compile time, never imported)
180444
"**/build.rs",

0 commit comments

Comments
 (0)