Skip to content

Commit 8d55de4

Browse files
castlerCopilot
andcommitted
Fix RULE-6-9-1 false positives for consistent type-alias redeclarations
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>
1 parent 464c304 commit 8d55de4

1 file changed

Lines changed: 77 additions & 0 deletions

File tree

cpp/misra/src/rules/RULE-6-9-1/TypeAliasesDeclaration.ql

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,91 @@
1717
import cpp
1818
import codingstandards.cpp.misra
1919

20+
/**
21+
* Holds if `decl1` and `decl2` refer to the same source location (same file,
22+
* line and column).
23+
*/
24+
predicate sameSourceLocation(DeclarationEntry decl1, DeclarationEntry decl2) {
25+
decl1.getLocation().getFile() = decl2.getLocation().getFile() and
26+
decl1.getLocation().getStartLine() = decl2.getLocation().getStartLine() and
27+
decl1.getLocation().getStartColumn() = decl2.getLocation().getStartColumn()
28+
}
29+
30+
/**
31+
* Gets a line, in the file of declaration entry `decl`, that is part of the
32+
* source range in which the type used for the declared entity appears.
33+
*
34+
* This is the range spanned by the declaration entry itself, extended - for a
35+
* function definition - up to the start of the function body. The extension is
36+
* required because a function definition may use a trailing return type
37+
* (`auto f() -> T`), which appears after the function name and hence outside the
38+
* declaration entry's own (name-based) location.
39+
*/
40+
predicate declTypeUseLine(DeclarationEntry decl, File file, int line) {
41+
file = decl.getLocation().getFile() and
42+
(
43+
line in [decl.getLocation().getStartLine() .. decl.getLocation().getEndLine()]
44+
or
45+
exists(FunctionDeclarationEntry fde |
46+
fde = decl and
47+
fde.getBlock().getLocation().getFile() = file and
48+
line in [fde.getLocation().getStartLine() .. fde.getBlock().getLocation().getStartLine()]
49+
)
50+
)
51+
}
52+
53+
/**
54+
* Holds if the type alias `t` is mentioned within the source lines spanned by
55+
* the declaration entry `decl`.
56+
*
57+
* `TypedefType.getATypeNameUse()` is documented to return a conservative
58+
* (incomplete) set of type name uses - in particular it omits uses on
59+
* prototypes and around template instantiations. As a result it frequently
60+
* fails to associate an alias with a redeclaration that genuinely uses it
61+
* (e.g. a function prototype in a header whose definition lives in a `.cpp`),
62+
* which produces false positives for this rule. `TypeMention` records every
63+
* syntactic mention of a type together with its location, so we use it to
64+
* confirm whether `decl` really does use the alias `t`.
65+
*/
66+
predicate typeAliasMentionedIn(TypedefType t, DeclarationEntry decl) {
67+
exists(TypeMention tm |
68+
// Match on the qualified name rather than object identity: a mention of a
69+
// type alias template (e.g. `Result`) resolves to the generic alias, while
70+
// `getATypeNameUse()` reports the instantiated alias (e.g. `Result<X>`).
71+
// These are distinct `TypedefType`s but the same alias spelling, which is
72+
// what this rule is concerned with.
73+
tm.getMentionedType().(TypedefType).getQualifiedName() = t.getQualifiedName() and
74+
tm.getLocation().getFile() = decl.getLocation().getFile() and
75+
declTypeUseLine(decl, tm.getLocation().getFile(), tm.getLocation().getStartLine())
76+
)
77+
}
78+
2079
from DeclarationEntry decl1, DeclarationEntry decl2, TypedefType t
2180
where
2281
not isExcluded(decl1, Declarations5Package::typeAliasesDeclarationQuery()) and
2382
not isExcluded(decl2, Declarations5Package::typeAliasesDeclarationQuery()) and
2483
not decl1 = decl2 and
2584
decl1.getDeclaration() = decl2.getDeclaration() and
85+
// Two declaration entries that share the exact same source location are the
86+
// same source declaration seen from different translation units, not two
87+
// redeclarations that could disagree on a type alias. Comparing them produces
88+
// false positives whenever `getATypeNameUse()` happens to associate the alias
89+
// with one copy but not the other.
90+
not sameSourceLocation(decl1, decl2) and
91+
// Declaration entries synthesised for template instantiations are not source
92+
// redeclarations and duplicate the entries of the uninstantiated template
93+
// (without recording their type name uses), so comparing them yields false
94+
// positives.
95+
not decl1.getDeclaration().isFromTemplateInstantiation(_) and
96+
not decl2.getDeclaration().isFromTemplateInstantiation(_) and
2697
t.getATypeNameUse() = decl1 and
2798
not t.getATypeNameUse() = decl2 and
99+
// `getATypeNameUse()` is incomplete, so it may report that `t` is not used on
100+
// `decl2` even when `decl2` uses exactly the same alias as `decl1`. Confirm via
101+
// `TypeMention` that `decl2` really does not mention `t` before reporting a
102+
// divergence, otherwise the same alias used on a prototype/definition pair is
103+
// wrongly flagged.
104+
not typeAliasMentionedIn(t, decl2) and
28105
//exception cases - we dont want to disallow struct typedef name use
29106
not t.getBaseType() instanceof Struct and
30107
not t.getBaseType() instanceof Enum

0 commit comments

Comments
 (0)