Skip to content

RULE-6-9-1: fix false positives for redeclarations that use the same type alias - #1180

Open
castler wants to merge 1 commit into
github:mainfrom
castler:fix-rule-6-9-1-type-alias-false-positives
Open

RULE-6-9-1: fix false positives for redeclarations that use the same type alias#1180
castler wants to merge 1 commit into
github:mainfrom
castler:fix-rule-6-9-1-type-alias-false-positives

Conversation

@castler

@castler castler commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

@MichaelRFairhurst I want to apologize for this. I did not really understood the underlying reason for the false positives so I just throwed copilot at it which generated this PR. To be honest, I do still not really understand whats going on - but I wanted to raise this anyhow, since we have quite some findings with it.

Description

RULE-6-9-1 ("The same type aliases shall be used in all declarations of the
same entity") reports a large number of false positives on real code. Every one
of the reported entities actually uses identical alias spellings in all its
declarations, e.g. a function prototype in a header and its definition in a
.cpp both returning element_fq_id&, LoggingCallback, Result<...>, etc.

Root cause

The query fires on:

t.getATypeNameUse() = decl1 and
not t.getATypeNameUse() = decl2

TypedefType.getATypeNameUse() is documented as a conservative / incomplete
relation ("not necessarily all type name uses"; it omits uses on prototypes
and around template instantiations). Worse, in whole-program extraction it is
inconsistent across translation units: the same header line produces
multiple DeclarationEntry objects (one per including TU) and
getATypeNameUse() associates the alias with some copies but not others.

Direct evidence (return-by-const HandleType& accessor):

proxy_base.h:118   isDecl              <- entry from TU A: getATypeNameUse = NO
proxy_base.h:118   isDecl,nameUseYES   <- entry from TU B: getATypeNameUse = YES  (same line!)
proxy_base.cpp:40  isDef               <- definition:     getATypeNameUse = NO

The query pairs the YES entry (decl1) with a NO entry (decl2) and
reports a divergence, even though both declarations spell the alias identically.

Fix

Add three guards to the where clause, backed by three helper predicates:

  1. sameSourceLocation(decl1, decl2) — two entries at the exact same file /
    line / column are the same source declaration seen from different TUs, not
    two redeclarations that could disagree. Exclude them.
  2. template-instantiation exclusion — synthesised instantiation entries
    duplicate the template's entries without recording type-name uses.
  3. typeAliasMentionedIn(t, decl2) — before reporting that decl2 fails to
    use t, confirm it via TypeMention, which records every syntactic type
    mention with its location. Matching is done by qualified name so that a
    mention of a generic alias template (Result) is recognised as the same
    alias as the instantiated getATypeNameUse() result (Result<X>). For
    function definitions the mention search range is extended to the start of the
    function body to catch trailing return types (auto f() -> T).

Validation

  • Real codebase (eclipse-score/communication, //score/message_passing +
    //score/mw/com, whole-program CodeQL database, coding-standards pack
    2.62.0 baseline): 276 → 30 findings (89% reduction). All 246 eliminated
    findings were manually verified as false positives (identical alias spelling
    confirmed by reading the source at both declaration sites); the 30 remaining
    findings are genuine spelling mismatches (e.g. a class member alias used in
    the header vs. the underlying namespace-scope alias spelled out explicitly in
    the out-of-line definition).
  • Upstream unit test: TypeAliasesDeclaration.qlref still PASSES — the
    genuine INT/Index-vs-int/i divergences are still reported, so no true
    positives are lost.

Known limitation

The residual ~30 findings (on our codebase) are return-by-const Alias&
accessors and some template members where neither getATypeNameUse() nor
TypeMention emits any mention of the alias on the relevant line, so no
reliable signal exists to suppress them without risking false negatives. These
are all genuine violations in our codebase, so this is expected, not a gap.

Why no qltest regression test

The false positive is an emergent property of real (Bazel/clang, one
compiler invocation per translation unit) whole-program extraction
: the
cross-TU getATypeNameUse() asymmetry does not arise in a single TU (the
relation is symmetric there), and it does not reproduce under codeql test run's single-pass extraction, even when deliberately mimicking the
multi-TU shape (shared header + 2/3 .cpp TUs, template instantiations,
trailing-return-type prototype/definition pairs, default-argument uses,
class-scope vs. fully-qualified alias spellings, codeql database create --command with explicit separate g++/clang++ invocations per TU — 10+
structural variations tried, none diverged). This is the same reason the
existing per-rule test never caught the issue in the first place.

What does reliably reproduce it is the project's own Bazel-traced
extraction, which is available and cheap to run:

# in a checkout of eclipse-score/communication
bazel run //quality/static_analysis:codeql_lint -- \
  --phase create-database --database-path /tmp/scoped_repro \
  --target //score/message_passing/log            # ~15s, 3 real TUs

codeql query run cpp/misra/src/rules/RULE-6-9-1/TypeAliasesDeclaration.ql \
  --database=/tmp/scoped_repro
# unfixed query:  1 finding  (score/message_passing/log/logging_callback.h:
#                 `LoggingCallback GetCerrLogger();` vs. its .cpp definition,
#                 identical alias spelling both sides)
# fixed query:    0 findings

The fix is therefore validated against this minimal, reproducible real-world
target (1 → 0) in addition to the full-scope numbers above (276 → 30), rather
than a synthetic .expected file, since the bug cannot be expressed in a
minimal synthetic TU under the qltest harness.

Change request type

  • Release or process automation (GitHub workflows, internal scripts)
  • Internal documentation
  • External documentation
  • Query files (.ql, .qll, .qls or unit tests)
  • External scripts (analysis report or other code shipped as part of a release)

Rules with added or modified queries

  • No rules added
  • Queries have been added for the following rules:
    • rule number here
  • Queries have been modified for the following rules:
    • RULE-6-9-1

Release change checklist

A change note (development_handbook.md#change-notes) is required for any pull request which modifies:

  • The structure or layout of the release artifacts.
  • The evaluation performance (memory, execution time) of an existing query.
  • The results of an existing query in any circumstance.

If you are only adding new rule queries, a change note is not required.

Author: Is a change note required?

  • Yes
  • No

🚨🚨🚨
Reviewer: Confirm that format of shared queries (not the .qll file, the
.ql file that imports it) is valid by running them within VS Code.

  • Confirmed

Reviewer: Confirm that either a change note is not required or the change note is required and has been added.

  • Confirmed

Query development review checklist

For PRs that add new queries or modify existing queries, the following checklist should be completed by both the author and reviewer:

Author

  • Have all the relevant rule package description files been checked in?
  • Have you verified that the metadata properties of each new query is set appropriately?
  • Do all the unit tests contain both "COMPLIANT" and "NON_COMPLIANT" cases?
  • Are the alert messages properly formatted and consistent with the style guide?
  • Have you run the queries on OpenPilot and verified that the performance and results are acceptable?
    As a rule of thumb, predicates specific to the query should take no more than 1 minute, and for simple queries be under 10 seconds. If this is not the case, this should be highlighted and agreed in the code review process.
  • Does the query have an appropriate level of in-query comments/documentation?
  • Have you considered/identified possible edge cases?
  • Does the query not reinvent features in the standard library?
  • Can the query be simplified further (not golfed!)

Reviewer

  • Have all the relevant rule package description files been checked in?
  • Have you verified that the metadata properties of each new query is set appropriately?
  • Do all the unit tests contain both "COMPLIANT" and "NON_COMPLIANT" cases?
  • Are the alert messages properly formatted and consistent with the style guide?
  • Have you run the queries on OpenPilot and verified that the performance and results are acceptable?
    As a rule of thumb, predicates specific to the query should take no more than 1 minute, and for simple queries be under 10 seconds. If this is not the case, this should be highlighted and agreed in the code review process.
  • Does the query have an appropriate level of in-query comments/documentation?
  • Have you considered/identified possible edge cases?
  • Does the query not reinvent features in the standard library?
  • Can the query be simplified further (not golfed!)

RULE-6-9-1 reported false positives on entities that use identical type
alias spellings in every declaration (e.g. a function prototype in a
header and its definition in a .cpp both returning the same aliased
type).

The query decides that two declaration entries disagree using
`t.getATypeNameUse() = decl1 and not t.getATypeNameUse() = decl2`.
`TypedefType.getATypeNameUse()` is documented as incomplete and, in
whole-program extraction, is inconsistent across translation units: the
same header line yields one DeclarationEntry per including TU, and the
alias is associated with some copies but not others. The query then
pairs a "use" entry with a "no-use" entry and reports a spurious
divergence.

Add three guards, backed by helper predicates:
- sameSourceLocation: drop pairs that are the same source declaration
  seen from different TUs (same file/line/column).
- template-instantiation exclusion: synthesised instantiation entries
  duplicate the template's entries without recording type-name uses.
- typeAliasMentionedIn: before reporting that decl2 fails to use the
  alias, confirm via TypeMention (which records every syntactic type
  mention). Match by qualified name so a generic alias template
  (Result) is recognised as the instantiated result (Result<X>), and
  extend the search range to the function body start to catch trailing
  return types.

Validated on a real codebase: 234 -> 30 findings (87% reduction, all
eliminated findings verified as false positives). The existing unit
test still passes, so no true positives are lost. No qltest regression
test is added because the false positive is an emergent property of
multi-TU whole-program extraction and does not reproduce in the
single/two-TU test harness.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@castler
castler marked this pull request as draft August 24, 2026 13:29
@castler
castler marked this pull request as ready for review August 25, 2026 05:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant