Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
159 commits
Select commit Hold shift + click to select a range
0df8508
test(const-param): a const suggestion must compile and keep the gener…
SizzleUnrlsd Oct 13, 2026
9a8fce8
fix(const-param): check each const suggestion with Clang before repor…
SizzleUnrlsd Oct 13, 2026
5311ff6
test(const-param): check suggestions in the compilation database, met…
SizzleUnrlsd Oct 13, 2026
ff42235
fix(const-param): recheck the compilation database and compare semant…
SizzleUnrlsd Oct 13, 2026
75043d6
test(const-param): pair specializations whose deduced type changes on…
SizzleUnrlsd Oct 13, 2026
eb17a79
fix(const-param): pair specializations whose deduced type changes
SizzleUnrlsd Oct 13, 2026
5abdbae
test(const-param): refuse collapsed specializations even with the sam…
SizzleUnrlsd Oct 13, 2026
5abb555
perf(const-param): parse the defining file's preamble once for all it…
SizzleUnrlsd Oct 13, 2026
abfff76
test(const-param): a header's checked warning is not new after the re…
SizzleUnrlsd Oct 14, 2026
8298c83
fix(const-param): name a diagnostic's file by its real path
SizzleUnrlsd Oct 14, 2026
9dc33cf
revert(const-param): drop the precompiled preamble
SizzleUnrlsd Oct 14, 2026
629344e
test(const-param): check const suggestions with Clang only under an o…
SizzleUnrlsd Oct 14, 2026
cce6c25
feat(const-param): add --experimental-const-suggestion-check
SizzleUnrlsd Oct 14, 2026
035bf98
ci(const-param): run the experimental const suggestion check on Linux…
SizzleUnrlsd Oct 14, 2026
10258fe
ci(const-param): say ci.yml runs these tests too, restore FetchConten…
SizzleUnrlsd Oct 14, 2026
f3599c3
test(const-param): homonyms, macros, void casts, CRLF, both modes, re…
SizzleUnrlsd Oct 14, 2026
ea14ccc
fix(const-param): fix the review's findings on the experimental check
SizzleUnrlsd Oct 14, 2026
9c1f00f
ci(const-param): drop the dedicated job, ci.yml runs the same tests
SizzleUnrlsd Oct 14, 2026
0239868
docs(const-param): a qualifier dropped in C is a warning, not a compi…
SizzleUnrlsd Oct 14, 2026
3347785
style(const-param): format the const-suggestion fixtures
SizzleUnrlsd Oct 14, 2026
693590a
refactor(const-param): take three read-only parameters as const
SizzleUnrlsd Oct 14, 2026
e72e517
ci(concurrency): run the analyzer image v0.8.0, which has Clang's hea…
SizzleUnrlsd Oct 15, 2026
2a5b0c1
Merge origin/main into 217-const-suggestion-compiles
SizzleUnrlsd Oct 15, 2026
7d96db1
docs(spec): design the second version of the const suggestion check
SizzleUnrlsd Oct 16, 2026
8b283a8
docs(spec): apply the review of the const suggestion check design
SizzleUnrlsd Oct 16, 2026
1ed722e
docs(plan): plan the second version of the const suggestion check
SizzleUnrlsd Oct 16, 2026
a0e3d48
docs(plan): apply the review of the const suggestion check plan
SizzleUnrlsd Oct 16, 2026
49f1e7c
docs(plan): take a parameter's type from its own declaration in P0
SizzleUnrlsd Oct 16, 2026
de8510a
docs(plan): align P0's test steps with their commits
SizzleUnrlsd Oct 16, 2026
90745aa
docs(plan): record the independent review of P0
SizzleUnrlsd Oct 16, 2026
64f89a4
docs(plan): align P0's last commits with the commit checker
SizzleUnrlsd Oct 16, 2026
64ed37e
Merge origin/main into 217-t-v2
SizzleUnrlsd Oct 16, 2026
a9c2e30
docs(plan): detail stage 1's texts, rulings and shared types
SizzleUnrlsd Oct 16, 2026
2f74b72
docs(plan): detail task 1.1, units and verdicts
SizzleUnrlsd Oct 16, 2026
eda8b5b
docs(plan): detail task 1.2, the parameter from P0
SizzleUnrlsd Oct 16, 2026
e70e0fc
docs(plan): detail task 1.3, planning and plan scope
SizzleUnrlsd Oct 16, 2026
b1aa87f
docs(plan): detail task 1.4, facts and the scope after the rewrite
SizzleUnrlsd Oct 16, 2026
0f90be0
docs(plan): detail task 1.5, correspondences and established pairs
SizzleUnrlsd Oct 16, 2026
135ad07
docs(plan): detail task 1.6, collisions and stale declarations
SizzleUnrlsd Oct 16, 2026
f0cac43
docs(plan): detail task 1.7, explicit address casts
SizzleUnrlsd Oct 16, 2026
2c52435
docs(plan): detail task 1.8, trace and README
SizzleUnrlsd Oct 16, 2026
3b07f48
docs(plan): extend D12's expectations to the block D14 adds
SizzleUnrlsd Oct 16, 2026
4e9bf48
docs(spec): limit the shared initial compilation to one module in sta…
SizzleUnrlsd Oct 17, 2026
e74d248
docs(plan): correct stage 1's rulings and its texts on weak definitions
SizzleUnrlsd Oct 17, 2026
eb3ec09
docs(plan): know a declaration by both positions, test incomplete ins…
SizzleUnrlsd Oct 17, 2026
e4a736c
docs(plan): pair inherited constructors by specialization, adapt unit…
SizzleUnrlsd Oct 17, 2026
8e8e877
docs(plan): name the strong definition that a weak one yields to
SizzleUnrlsd Oct 17, 2026
a93211c
docs(plan): count the tests the review added in stage 1's validation
SizzleUnrlsd Oct 17, 2026
668a849
test(const-param): name the one configuration of a file listed twice
SizzleUnrlsd Oct 17, 2026
23be27d
test(const-param): compile each file once as it is for all its sugges…
SizzleUnrlsd Oct 17, 2026
cb82201
refactor(const-param): judge each unit and aggregate three verdicts
SizzleUnrlsd Oct 17, 2026
7772fac
feat(const-param): name each unit and its configuration in the trace
SizzleUnrlsd Oct 17, 2026
ca49db0
perf(const-param): compile each other file once as it is for all sugg…
SizzleUnrlsd Oct 17, 2026
1050cca
test(const-param): check the parameter a C2 constructor's suggestion …
SizzleUnrlsd Oct 17, 2026
514605a
fix(const-param): rewrite the parameter at the source index P0 gives
SizzleUnrlsd Oct 17, 2026
4b3f050
refactor(const-param): drop the debug number that the source index re…
SizzleUnrlsd Oct 17, 2026
229b2bf
test(const-param): keep a suggestion whose macro argument types two p…
SizzleUnrlsd Oct 17, 2026
8930954
test(const-param): a backslash and spaces still continue a macro defi…
SizzleUnrlsd Oct 17, 2026
066847a
refactor(const-param): move the planning of a suggestion to its own file
SizzleUnrlsd Oct 17, 2026
84462e2
fix(const-param): find macro definitions with the preprocessor, not t…
SizzleUnrlsd Oct 17, 2026
bdeff08
fix(const-param): leave a rewrite that reaches another parameter unch…
SizzleUnrlsd Oct 17, 2026
e178353
docs(plan): record task 1.3's macro index and parameter-only scope
SizzleUnrlsd Oct 17, 2026
232dfb3
test(const-param): check a member operator's suggestion in the file t…
SizzleUnrlsd Oct 19, 2026
957213c
test(const-param): a C homonym with another linkage is another function
SizzleUnrlsd Oct 19, 2026
7288562
test(const-param): a changed call or constant refutes even with the s…
SizzleUnrlsd Oct 19, 2026
5b84829
test(const-param): keep a rewrite that changes another file's declara…
SizzleUnrlsd Oct 19, 2026
05a7bc0
test(const-param): keep a suggestion whose rewrite stops at a fatal e…
SizzleUnrlsd Oct 19, 2026
53d6f00
test(const-param): tell apart two functions that one macro invocation…
SizzleUnrlsd Oct 19, 2026
641f7cf
refactor(const-param): move the compilation and its inspection to the…
SizzleUnrlsd Oct 19, 2026
2de657f
fix(const-param): find the function by its declarations, not by its name
SizzleUnrlsd Oct 19, 2026
7fb25d8
feat(const-param): refute a suggestion that changes a call, override …
SizzleUnrlsd Oct 19, 2026
ba93b73
feat(const-param): leave a rewrite that changes another declaration u…
SizzleUnrlsd Oct 19, 2026
2861bf8
test(const-param): compare each reference that a macro expands twice …
SizzleUnrlsd Oct 19, 2026
cbee390
test(const-param): keep a rewrite that makes another declaration inva…
SizzleUnrlsd Oct 19, 2026
9fadb09
fix(const-param): compare one to one the facts that one site repeats
SizzleUnrlsd Oct 19, 2026
79ac3ef
fix(const-param): compare the type of a declaration the rewrite makes…
SizzleUnrlsd Oct 19, 2026
a5f28cb
test(const-param): keep unchecked a fact that the rewrite leaves with…
SizzleUnrlsd Oct 20, 2026
1cabf7e
docs(plan): add the two reasons of an incomplete comparison to the texts
SizzleUnrlsd Oct 20, 2026
335ff47
test(const-param): check a suggestion through the constructors that i…
SizzleUnrlsd Oct 20, 2026
6863d60
test(const-param): keep the suggestions whose specializations merge, …
SizzleUnrlsd Oct 20, 2026
50a98f0
test(const-param): keep a suggestion whose code change nothing explai…
SizzleUnrlsd Oct 20, 2026
105487a
feat(const-param): record the symbols a rewrite may rename, with thei…
SizzleUnrlsd Oct 20, 2026
990b6a9
feat(const-param): establish the correspondences of a rewrite from it…
SizzleUnrlsd Oct 20, 2026
2deae59
fix(const-param): compare the code through established pairs, never r…
SizzleUnrlsd Oct 20, 2026
7741c25
fix(const-param): pair the specialization that an inheriting construc…
SizzleUnrlsd Oct 20, 2026
d711321
test(const-param): an empty parameter may take an IR argument, as on …
SizzleUnrlsd Oct 20, 2026
46c036f
Merge branch '237-parameter-binding-abi' into 217-t-v2
SizzleUnrlsd Oct 20, 2026
6be6113
test(const-param): keep a rewrite that takes another file's symbol, u…
SizzleUnrlsd Oct 20, 2026
2bc1d29
test(const-param): a function a shared header defines does not collid…
SizzleUnrlsd Oct 20, 2026
291d280
test(const-param): keep a suggestion that another file redeclares, un…
SizzleUnrlsd Oct 20, 2026
2b4b968
feat(const-param): keep a rewrite that may collide with another file,…
SizzleUnrlsd Oct 20, 2026
79dad0f
fix(const-param): keep a suggestion that another file redeclares, unc…
SizzleUnrlsd Oct 20, 2026
216b68b
docs(readme): a static function and its homonym elsewhere are two fun…
SizzleUnrlsd Oct 20, 2026
01342a4
test(const-param): keep a rewrite that merges another declaration, un…
SizzleUnrlsd Oct 20, 2026
6b7df64
test(const-param): name the definition that a link keeps, strong agai…
SizzleUnrlsd Oct 20, 2026
b3504ea
test(const-param): keep a suggestion whose address another file casts…
SizzleUnrlsd Oct 20, 2026
af99724
fix(const-param): keep a suggestion whose address is cast, unchecked
SizzleUnrlsd Oct 20, 2026
7534472
test(const-param): a cast in the defining file hides no other file's …
SizzleUnrlsd Oct 20, 2026
a60a1b4
fix(const-param): stop judging the units at a refutation only
SizzleUnrlsd Oct 20, 2026
1f81438
test(const-param): an explicit cast keeps a suggestion, not checked
SizzleUnrlsd Oct 20, 2026
da1f16e
docs(plan): only a refutation stops the judgement of the units
SizzleUnrlsd Oct 21, 2026
6f931f6
test(const-param): give each verdict's grounds in the trace
SizzleUnrlsd Oct 21, 2026
f91dd31
feat(const-param): trace the suggestion, its rewrite, its units and i…
SizzleUnrlsd Oct 21, 2026
00a21b7
docs(readme): describe the three verdicts of the const suggestion check
SizzleUnrlsd Oct 21, 2026
b0d5cda
docs(plan): open the draft pull requests after stage 1
SizzleUnrlsd Oct 21, 2026
519500a
refactor(const-param): take the visitors' read-only parameters as const
SizzleUnrlsd Oct 21, 2026
6a91a79
test(analysis): demangle a C++23 explicit object member on any C++ ru…
SizzleUnrlsd Oct 21, 2026
270955e
fix(analysis): fall back to LLVM's demangler when the C++ runtime's f…
SizzleUnrlsd Oct 21, 2026
4fd0dae
Merge branch '240-demangle-fallback' into 217-t-v2
SizzleUnrlsd Oct 21, 2026
9f723c6
docs(spec): keep Clang's default errors in the tracked groups, add D1…
SizzleUnrlsd Oct 21, 2026
de957e7
docs(plan): detail stage 2, its rulings and batch 2.1's tests
SizzleUnrlsd Oct 21, 2026
43759b0
test(const-param): check a suggestion despite -Werror and a disabled …
SizzleUnrlsd Oct 21, 2026
fc4d7be
test(const-param): a tracked group made an error stays a warning for …
SizzleUnrlsd Oct 21, 2026
5b74b31
fix(const-param): keep the tracked warning groups as Clang reports th…
SizzleUnrlsd Oct 21, 2026
f849c36
docs(plan): keep C's tag keywords in the displayed candidate, write o…
SizzleUnrlsd Oct 22, 2026
ada576a
test(const-param): check a C function that a C++ file declares throug…
SizzleUnrlsd Oct 22, 2026
ead913f
test(const-param): check declarations that differ in a top-level qual…
SizzleUnrlsd Oct 22, 2026
095d23c
fix(const-param): expect each declaration's own candidate, in its uni…
SizzleUnrlsd Oct 22, 2026
361cdca
docs(plan): leave merged declarations to the collisions in Task 2.2
SizzleUnrlsd Oct 22, 2026
0e64048
test(const-param): detect a rewrite that loses the pointer's own top-…
SizzleUnrlsd Oct 22, 2026
e998a14
test(const-param): expect the const reference alone for an rvalue ref…
SizzleUnrlsd Oct 22, 2026
a900e8f
test(const-param): display the candidate of a const pointer to a read…
SizzleUnrlsd Oct 22, 2026
4c72332
fix(const-param): display the plan's candidate for rvalue and const p…
SizzleUnrlsd Oct 22, 2026
0d66e28
test(const-param): check a C pointer typedef with a qualifier written…
SizzleUnrlsd Oct 22, 2026
4192a15
test(const-param): check a C++ pointer typedef with a qualifier writt…
SizzleUnrlsd Oct 22, 2026
b17ff2a
fix(const-param): fold a pointer typedef's separate qualifiers into t…
SizzleUnrlsd Oct 22, 2026
fe43067
style(test): format the D10 typedef fixtures
SizzleUnrlsd Oct 22, 2026
a6a2160
test(const-param): pin the multiplicity and order of correspondences
SizzleUnrlsd Oct 22, 2026
48f3ec1
perf(const-param): index the correspondences' references once per call
SizzleUnrlsd Oct 22, 2026
33e288c
test(const-param): refute a rewrite that breaks a call a deduced decl…
SizzleUnrlsd Oct 23, 2026
86f629f
test(const-param): detect written parts that the rewrite changes outs…
SizzleUnrlsd Oct 23, 2026
5f0ccf7
docs(spec): separate a type's written form from its deduced part (D19)
SizzleUnrlsd Oct 23, 2026
7010ff6
fix(const-param): judge a type's written form, and the types that the…
SizzleUnrlsd Oct 23, 2026
5167998
test(const-param): keep a planned parameter's default argument outsid…
SizzleUnrlsd Oct 23, 2026
4454947
test(const-param): check the correspondences of nested entities end t…
SizzleUnrlsd Oct 25, 2026
7511f50
feat(const-param): pair the entities nested in established pairs (§5.…
SizzleUnrlsd Oct 25, 2026
c8d7883
test(const-param): pin the keys of nested and implicit-member identities
SizzleUnrlsd Oct 25, 2026
d20c855
docs(spec): pair the entities nested in established pairs (§5.7, D20)
SizzleUnrlsd Oct 25, 2026
72dd688
Merge origin/main into 217-t-v2
SizzleUnrlsd Oct 26, 2026
bed265d
docs: bring the plan's status, the spec and the README up to date
SizzleUnrlsd Oct 26, 2026
bb068c8
docs(plan): record the #215 reserve, the deferred invocation and the …
SizzleUnrlsd Oct 26, 2026
60a9828
test(analysis): cover only-declared dependencies, operators and faile…
SizzleUnrlsd Oct 28, 2026
3266376
fix(analysis): keep a suggestion not checked when a renamed callee is…
SizzleUnrlsd Oct 28, 2026
a01fbf5
fix(analysis): find a free operator declared again in a file of the d…
SizzleUnrlsd Oct 28, 2026
c38fd58
fix(analysis): report a rewrite whose file cannot be read or has changed
SizzleUnrlsd Oct 28, 2026
42c9a4d
fix(analysis): keep a failed rewrite not checked when it adds no error
SizzleUnrlsd Oct 28, 2026
42fa604
docs(analysis): state the const suggestion check's verdicts in --help…
SizzleUnrlsd Oct 28, 2026
4f95305
Merge origin/main into 217-t-v2
SizzleUnrlsd Oct 30, 2026
94deb0d
test(analysis): cover identifiers starting with operator and -isystem…
SizzleUnrlsd Oct 31, 2026
902e12d
fix(analysis): tell an operator's name from an identifier by Clang's …
SizzleUnrlsd Oct 31, 2026
6db974b
fix(analysis): name a failed rewrite's first error, not an earlier wa…
SizzleUnrlsd Oct 31, 2026
c56c99d
docs(analysis): state the check's exceptions, the operator lookup and…
SizzleUnrlsd Oct 31, 2026
2318ea2
Merge origin/main into 217-t-v2
SizzleUnrlsd Oct 31, 2026
5dcbae0
test(analysis): check two callbacks that a rewrite makes differ in a …
SizzleUnrlsd Nov 1, 2026
af1edbe
fix(analysis): track pointer-type-mismatch in the const suggestion check
SizzleUnrlsd Nov 1, 2026
10adf29
test(analysis): check a file of the database that imports a module
SizzleUnrlsd Nov 1, 2026
8658cd0
fix(analysis): keep a suggestion not checked when a file imports a mo…
SizzleUnrlsd Nov 1, 2026
8f875a7
style(analysis): format the two fixes of the const suggestion check
SizzleUnrlsd Nov 1, 2026
c84b959
test(analysis): check a defining file that imports a module
SizzleUnrlsd Nov 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/concurrency.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ jobs:
- name: Analyze
uses: CoreTrace/coretrace-concurrency-analyzer@v0
with:
# The first image with Clang's headers, which ConstSuggestionCheck.cpp
# includes (CoreTrace/coretrace-concurrency-analyzer#204).
version: v0.8.0
compile-commands: build/compile_commands.json
fail-on: error
# A pull request from a fork gets a read-only token, which Code Scanning refuses.
Expand Down
23 changes: 23 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(LLVM_LINK_LLVM_DYLIB ON)

find_package(LLVM REQUIRED CONFIG)
# The check of const suggestions drives Clang itself (#217).
find_package(Clang REQUIRED CONFIG)

message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
Expand Down Expand Up @@ -74,6 +76,11 @@ set(STACK_ANALYZER_SOURCES
src/analysis/AnalyzerUtils.cpp
src/analysis/CompileCommands.cpp
src/analysis/ConstParamAnalysis.cpp
src/analysis/ConstSuggestionCheck.cpp
src/analysis/ConstSuggestionCode.cpp
src/analysis/ConstSuggestionFacts.cpp
src/analysis/ConstSuggestionMatch.cpp
src/analysis/ConstSuggestionPlan.cpp
src/analysis/CommandInjectionAnalysis.cpp
src/analysis/DuplicateIfCondition.cpp
src/analysis/DynamicAlloca.cpp
Expand Down Expand Up @@ -194,6 +201,7 @@ target_include_directories(stack_usage_analyzer_lib
target_include_directories(stack_usage_analyzer_lib SYSTEM
PUBLIC
${LLVM_INCLUDE_DIRS}
${CLANG_INCLUDE_DIRS}
)

# Treat external dependency headers as system includes to reduce warning noise
Expand Down Expand Up @@ -236,6 +244,21 @@ else()
coretrace::logger
)
endif()
# The same Clang libraries as compilerlib links.
if(CLANG_LINK_CLANG_DYLIB)
target_link_libraries(stack_usage_analyzer_lib PUBLIC clang-cpp)
else()
target_link_libraries(stack_usage_analyzer_lib
PUBLIC
clangAST
clangBasic
clangCodeGen
clangDriver
clangFrontend
clangLex
clangSema
)
endif()

# # ===== CLI BINARY =====
# add_executable(stack_usage_analyzer
Expand Down
174 changes: 174 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ A model path given to one of these three options that is not a readable file sto
--no-resource-cross-tu disables cross-TU resource summaries
--resource-summary-cache-dir=<path> sets cache directory for cross-TU resource summaries (default: .cache/resource-lifetime)
--resource-summary-cache-memory-only keeps cross-TU summary cache in memory only (process-local, no files)
--experimental-const-suggestion-check checks each const parameter suggestion with Clang before reporting it (experimental, off by default; see "Experimental: const parameter suggestions checked with Clang")
--compile-ir-cache-dir=<path> enables dependency-aware LLVM IR compile cache for unchanged source files
--compile-ir-format=bc|ll selects source compilation IR format (`bc` default, `ll` for textual LLVM IR)
--timing prints compile/analysis timings to stderr, including aggregated hotspot ranking
Expand Down Expand Up @@ -345,6 +346,7 @@ Supported keys:
- `uninitialized-cross-tu`
- `resource-summary-cache-dir`
- `resource-summary-cache-memory-only`
- `experimental-const-suggestion-check`
- `compile-ir-cache-dir`
- `compile-ir-format` (`bc` or `ll`)

Expand Down Expand Up @@ -631,6 +633,178 @@ Example run:
For test files, `run_test.py` also supports per-file model selection with:
`// resource-model: <path>`.

### Experimental: const parameter suggestions checked with Clang

`ConstParameterNotModified` finds, in the LLVM IR, the pointer and reference parameters through
which nothing is written. The IR does not show every constraint that C and C++ put on a parameter:
the suggested `const` type may not compile (a callback of the old type, or `&p` passed as `int**`
in C++), may only drop a qualifier with a warning (the same `&p` in C), or may compile and select
other code (an overload, an override, `sizeof`, `if constexpr`, `_Generic`).

Without `--experimental-const-suggestion-check`, the default, nothing beyond the IR analysis is
checked: every suggestion is reported as the IR analysis finds it, without a check line, including
those whose suggested type does not compile, drops a qualifier, or changes the generated code.

With `--experimental-const-suggestion-check` (or `experimental-const-suggestion-check=true` in a
config file), each suggestion is checked with Clang before it is reported. The check is
experimental:
- it judges the rewrite from what Clang compiles before and after it: its errors and warnings,
what calls and references target, what methods override, constant conditions, and the generated
code. A difference that the facts do not explain keeps the suggestion as not checked. A change
that they show removes it, even when the program would behave the same;
- it compiles each file that sees the change once as it is, for all the suggestions of an analyzed
file, and once more for each suggestion. On this repository's self-analysis, on 4 CPUs, it adds
about 25 to 30% to the wall time.

The option is a trade-off, not a fix: with it, a valid suggestion can still be removed or kept as
not checked; without it, suggestions whose type does not compile, drops a qualifier, or changes the
generated code are reported.

Each suggestion gets one of three verdicts, which step 3 below decides in its order:
- verified: the suggestion is kept, with a `checked ...` line. In every file that sees the change,
the rewrite compiles, adds none of the tracked warnings, changes none of the facts below, and the
generated code matches;
- refuted: the suggestion is removed. In one of these files, the rewrite adds an error or a tracked
warning, or changes what a call or a reference targets, what a method of that name overrides, an
`if constexpr` condition, or the association that a `_Generic` selection chooses;
- indeterminate: the suggestion is kept, with `not checked with Clang: <reason>`. It then rests on
the IR analysis alone: it is not shown to be valid.

The check runs these steps:

1. The plan. The function is found in the defining file's compilation by its symbol. Every
declaration of it that this file sees, in the file and its headers, is rewritten in memory with
the type the check tries: a pointer to const (`const T *p`; `T *const p` becomes
`const T *const p`), or a reference to const (`const T &r`, for an rvalue reference `T &&r` too):
- a type written in a macro argument is rewritten where it is written;
- a type written by a macro that writes only the parameter gets `const` before the invocation;
- a typedef of a plain pointer is replaced by the type it names, made const, with the
qualifiers written beside it; one written by a macro leaves the suggestion not checked.

A macro definition is never rewritten, nor a macro argument that writes another parameter's type
too: the suggestion is then not checked.
2. The files. Every file that sees the change is compiled as it is and with the rewrite, with its
own compile command, at `-O0`. The files considered are the analyzed ones and, with
`--compile-commands`, every C or C++ file of the compilation database, analyzed or not. A file
sees the change when it reads a rewritten file, when its analyzed code references the function,
or, for a function outside any class with external linkage, when it names the function after
preprocessing, in its own text or in any header it includes, system headers included. An
operator's name is not an identifier: for an operator, a file sees the change when it writes the
keyword `operator`, which most C++ files do through the standard headers they include. A
suggestion on such an operator then compiles most C++ files of the compilation database as they
are and with the rewrite, which can cost far more than for a function named by an identifier,
and any of them that Clang cannot compile as it is leaves the suggestion not checked. A function
with internal linkage (`static`, anonymous namespace) and a function of the same name in another
file are two functions, whichever of them has internal linkage. Preprocessing does not read a
module that a file imports: a Clang header module, a C++20 header unit or named module, the
interface that a module implementation unit imports included. A file that imports one, and does
not see the change otherwise, leaves the suggestion not checked, unless another file refutes it.
This does not apply when the rewrite changes only the defining file and the function is not a
free function with external linkage: no other file can declare it then.
3. The judgement, in each file, in this order:
- a file that cannot be preprocessed, or does not compile as it is, cannot be checked, nor a file
whose compilation with the rewrite stops at a fatal error: not checked;
- the rewrite changes only what it plans: a rewritten declaration that does not get the type
tried, or another declaration of a rewritten file whose type changes, leaves it not checked;
- an error, or one of these warnings, that the rewrite adds: refuted. The tracked warnings are
`incompatible-pointer-types-discards-qualifiers`, `incompatible-pointer-types`,
`incompatible-function-pointer-types`, `int-conversion`, `overloaded-virtual` and
`pointer-type-mismatch` (in C, a conditional between two callbacks that the rewrite of one
makes differ). Whatever the compile command says, the check enables them and keeps the level
Clang gives them by default: a warning stays a warning under `-Werror`, and an error stays an
error;
- a compilation with the rewrite that fails without an error that the rewrite adds: not checked;
- a call or a reference that targets another function, a method of that name that overrides
other methods, an `if constexpr` condition or a `_Generic` selection that changes: refuted.
Facts that cannot be compared one to one leave the file not checked;
- a file that declares the function itself, outside the rewritten files, with its current type,
or that converts the function's address with an explicit cast, other than to `void`: not
checked;
- the symbols that the rewrite renames are paired from these facts, and from what Clang
identifies of the lambdas and local classes nested in paired functions: their members, the
specializations whose template arguments or type hold them, and the implicit members of the
classes that hold them. Every other part of such an identity must match. One left without a
pair, or two that would pair with the same symbol, leaves the file not checked;
- the generated code is compared through those pairs. It matches: verified, unless a symbol
that the rewrite renames, other than the function's own and those of the constructors that
inherit it, is only declared in the file, before and after the rewrite: nothing shows that
another file defines the renamed one (`h<int *>` becoming `h<const int *>`), so not checked.
A difference that none of these facts explains: not checked.

Once every file is judged, a symbol that the rewrite gives the function and that another file
defines, or another declaration that the rewrite merges into the function, could collide if the
files are linked together: not checked, and the reason states that assumption. The suggestion is
refuted if one file refutes it, otherwise not checked if one file or one collision leaves it
so, otherwise verified. Only a refutation stops the check of the other files.

Generated code is compared function by function and global by global, after these
normalizations:
- debug information is not generated;
- two kinds of metadata that do not change what the program does are removed: the source position
of inline assembly (`!srcloc`), used only to report errors, and annotations (`!annotation`),
which the LLVM Language Reference defines as without effect on the program;
- alias scopes and their domains lose their descriptive names, which identify nothing: each one
stays a distinct scope;
- every other metadata, module flags included, is compared by contents, not by number;
- attribute groups are compared by contents, not by numbers;
- values are numbered, not named, and comments of the printed IR are ignored;
- the symbols that the rewrite renames are paired, as step 3 establishes them: the function's own
symbols, the constructors that inherit it, what is named inside it (lambdas, local classes,
static locals), and the specializations that its calls instantiate. A renamed symbol without a
pair is a difference.

Two differences that a program can observe are accepted, each only once verified, and the message
lists them:
- the name that `typeid` gives a type named after the function: it holds the function's mangled
name;
- a string constant whose only change is the signature that `__PRETTY_FUNCTION__` prints.

The check covers only:
- the suggestions it checks. A suggestion marked `not checked with Clang: ...` rests on the IR
analysis alone, and its reason says why;
- the files that see the change, with their compile commands, and the code those configurations
compile. Other configurations, other targets, code that the preprocessor excludes, and files
outside both the analysis and the compilation database are not checked.

Its limits:
- it does not know which files are linked together, and does not decide the one-definition rule: a
possible collision between files keeps the suggestion as not checked;
- it checks the one configuration that the analysis loads for each file: a file that the
compilation database lists twice is checked with one of its commands;
- a rewrite that changes the program's behavior in a way that calls, overrides and constant
conditions do not show is kept as not checked, not removed: `sizeof(__PRETTY_FUNCTION__)`, for
example, changes with the rewrite;
- a file that sees the changes of two analyzed files is compiled as it is once for each, and a hit
of the IR cache gives no syntax tree: the check always compiles from the sources.

A CI job that enables the option checks the projects and configurations it analyzes, and nothing
else: an analysis run without the option, by a user or by another job, is not checked.

In this repository, `ci.yml` runs the whole `run_test.py` suite on Linux and macOS. It checks the
default behavior, and the check's tests with the option: the const-suggestion fixtures declare it
(`// analyzer-arg:`), and the checks pass it. These tests expect suggestions kept once checked,
suggestions removed, and suggestions kept as not checked, and analyze the same inputs without the
option too. They cover these fixtures, with their compile commands and the LLVM 20 of each runner;
they do not check this repository's sources, other projects or other configurations.

The message says how the suggestion was checked:
- `checked in the analyzed files only, with their compile commands: ...`, without a compilation
database;
- `checked in every file of the compilation database that sees the change, with its compile
command: ...`, with one;
- `difference: ...`, for each accepted difference;
- `ABI: ...`, for a C++ function with external linkage: its symbol changes, so code compiled
separately must use the updated declaration and be recompiled;
- `not checked with Clang: ...`, when the check could not run or could not conclude: for example on
an LLVM IR input (`.ll`, `.bc`), which has no source, for a file of the compilation database that
cannot be preprocessed, or for a possible collision between files. The suggestion then rests on
the IR analysis alone.

With `--verbose`, the trace gives each verdict's grounds, on `[const-suggestion]` lines: the
suggestion (its rule, its function, the parameter's position among those the source declares), the
type it tries, where the rewrite writes it, each file judged with its configuration and its
verdict, then the verdict and its reason.

### Example

Given this code:
Expand Down
Loading
Loading