Skip to content

Commit 0080da8

Browse files
Implement 0-2-3, unused type with limited visibility.
1 parent e140430 commit 0080da8

File tree

14 files changed

+489
-3
lines changed

14 files changed

+489
-3
lines changed

c/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
| test.c:7:18:7:18 | D | Type declaration D is not used. |
33
| test.c:28:11:28:11 | R | Type declaration R is not used. |
44
| test.c:41:12:41:12 | (unnamed class/struct/union) | Type declaration (unnamed class/struct/union) is not used. |
5+
| test.c:56:3:56:4 | T2 | Type declaration T2 is not used. |

c/common/test/rules/unusedtypedeclarations/test.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,13 @@ void test_nested_struct() {
4949
s.f1;
5050
s.f3;
5151
s.f5;
52-
}
52+
}
53+
54+
typedef struct {
55+
int m1;
56+
} T2; // NON_COMPLIANT
57+
typedef struct {
58+
int m1;
59+
} T1; // COMPLIANT - used in function below
60+
61+
void test_typedef() { T1 t1; }
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
- `RULE-2-3`, `A0-1-6` - `UnusedTypeDeclarations.ql`:
2+
- Type usage analysis has been improved to find more possible type usages, including:
3+
- Previous behavior considered anonymous types in variable declarations to be considered used by the variable definition itself. This has been improved to require that a field of the anonymous type is accessed for the type to be considered used.
4+
- Usages of a template type inside a specialization of that template are no longer considered usages of the template type.
5+
- Hidden friend declarations are no longer considered usages of the class they are declaring friendship for.
6+
- Improved exclusions generally, for cases such as nested types and functions within functions. These previously were a source of incorrectly identified type uses.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import cpp
2+
3+
/**
4+
* The last declaration in a class by location order.
5+
*
6+
* This may fail to capture certain cases such as hidden friends (see HiddenFriend.qll).
7+
*/
8+
class LastClassDeclaration extends Declaration {
9+
Class cls;
10+
11+
pragma[nomagic]
12+
LastClassDeclaration() {
13+
this =
14+
max(Declaration decl, Location l |
15+
decl = cls.getADeclaration() and
16+
l = decl.getLocation()
17+
|
18+
decl order by l.getEndLine(), l.getEndColumn()
19+
)
20+
}
21+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/**
2+
* The C++ extractor does not support hidden friends, which are friend functions defined within a
3+
* class, rather than declared:
4+
*
5+
* ```cpp
6+
* struct A {
7+
* friend void hidden_friend(A) {} // Definition: this is a hidden friend
8+
* friend void not_hidden_friend(A); // Declaration: this is not a hidden friend
9+
* };
10+
* ```
11+
*
12+
* In the database, a `FriendDecl` is not created for the hidden friend. The hidden friend function
13+
* is created as a `TopLevel` function with no enclosing element. However, we can identify it as a
14+
* hidden friend by its location.
15+
*/
16+
17+
import cpp
18+
import codingstandards.cpp.ast.Class
19+
20+
class PossibleHiddenFriend extends HiddenFriendCandidate {
21+
ClassCandidate cls;
22+
23+
PossibleHiddenFriend() { hidesFriend(cls, this) }
24+
25+
Class getFriendClass() { result = cls }
26+
}
27+
28+
/**
29+
* Begin by limiting the number of candidate functions to consider.
30+
*
31+
* Only inline top level functions can be hidden friends.
32+
*/
33+
private class HiddenFriendCandidate extends TopLevelFunction {
34+
HiddenFriendCandidate() { this.isInline() }
35+
}
36+
37+
/**
38+
* Only consider files which contain hidden friend candidates.
39+
*/
40+
private class FileCandidate extends File {
41+
FileCandidate() { exists(HiddenFriendCandidate c | c.getFile() = this) }
42+
}
43+
44+
/**
45+
* Only consider classes in candidate files, that include hidden friend candidates.
46+
*/
47+
private class ClassCandidate extends Class {
48+
ClassCandidate() { getFile() instanceof FileCandidate }
49+
50+
Declaration getNextSiblingDeclaration() {
51+
exists(LastClassDeclaration last |
52+
last.getDeclaringType() = this and
53+
result =
54+
min(Declaration decl |
55+
decl.getEnclosingElement() = this.getEnclosingElement() and
56+
pragma[only_bind_out](decl.getFile()) = pragma[only_bind_out](this.getFile()) and
57+
decl.getLocation().getStartLine() > last.getLocation().getEndLine()
58+
|
59+
decl order by decl.getLocation().getStartLine(), decl.getLocation().getStartColumn()
60+
)
61+
)
62+
}
63+
64+
Declaration getNextOrphanedDeclaration() {
65+
exists(LastClassDeclaration last |
66+
last.getDeclaringType() = this and
67+
result =
68+
min(OrphanedDeclaration decl, Location locLast, Location locDecl |
69+
orphanHasFile(decl, this.getFile()) and
70+
locDecl = decl.getLocation() and
71+
locLast = last.getLocation() and
72+
locLast.getStartLine() < locDecl.getEndLine()
73+
|
74+
decl order by locDecl.getStartLine(), locDecl.getStartColumn()
75+
)
76+
)
77+
}
78+
79+
Declaration getFirstNonClassDeclaration() {
80+
exists(LastClassDeclaration last |
81+
last.getDeclaringType() = this and
82+
result =
83+
min(Declaration decl |
84+
decl = getNextSiblingDeclaration() or decl = getNextOrphanedDeclaration()
85+
|
86+
decl order by decl.getLocation().getStartLine(), decl.getLocation().getStartColumn()
87+
)
88+
)
89+
}
90+
}
91+
92+
pragma[nomagic]
93+
private predicate orphanHasFile(OrphanedDeclaration orphan, FileCandidate file) {
94+
orphan.getFile() = file
95+
}
96+
97+
private class OrphanedDeclaration extends Declaration {
98+
OrphanedDeclaration() {
99+
not exists(getEnclosingElement()) and
100+
not this instanceof HiddenFriendCandidate and
101+
getFile() instanceof FileCandidate and
102+
not isFromTemplateInstantiation(_)
103+
}
104+
}
105+
106+
pragma[nomagic]
107+
private predicate classCandidateHasFile(ClassCandidate c, FileCandidate f) { c.getFile() = f }
108+
109+
pragma[nomagic]
110+
private predicate hiddenFriendCandidateHasFile(HiddenFriendCandidate h, FileCandidate f) {
111+
h.getFile() = f
112+
}
113+
114+
pragma[nomagic]
115+
private predicate hidesFriend(ClassCandidate c, HiddenFriendCandidate f) {
116+
exists(FileCandidate file, Location cloc, Location floc |
117+
classCandidateHasFile(c, file) and
118+
hiddenFriendCandidateHasFile(f, file) and
119+
cloc = c.getLocation() and
120+
floc = f.getLocation() and
121+
cloc.getEndLine() < floc.getStartLine() and
122+
floc.getEndLine() < c.getFirstNonClassDeclaration().getLocation().getStartLine()
123+
)
124+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
2+
import cpp
3+
import RuleMetadata
4+
import codingstandards.cpp.exclusions.RuleMetadata
5+
6+
newtype DeadCode9Query = TUnusedTypeWithLimitedVisibilityQuery()
7+
8+
predicate isDeadCode9QueryMetadata(Query query, string queryId, string ruleId, string category) {
9+
query =
10+
// `Query` instance for the `unusedTypeWithLimitedVisibility` query
11+
DeadCode9Package::unusedTypeWithLimitedVisibilityQuery() and
12+
queryId =
13+
// `@id` for the `unusedTypeWithLimitedVisibility` query
14+
"cpp/misra/unused-type-with-limited-visibility" and
15+
ruleId = "RULE-0-2-3" and
16+
category = "advisory"
17+
}
18+
19+
module DeadCode9Package {
20+
Query unusedTypeWithLimitedVisibilityQuery() {
21+
//autogenerate `Query` type
22+
result =
23+
// `Query` type for `unusedTypeWithLimitedVisibility` query
24+
TQueryCPP(TDeadCode9PackageQuery(TUnusedTypeWithLimitedVisibilityQuery()))
25+
}
26+
}

cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import Conversions2
1919
import DeadCode
2020
import DeadCode3
2121
import DeadCode4
22+
import DeadCode9
2223
import Declarations
2324
import ExceptionSafety
2425
import Exceptions1
@@ -91,6 +92,7 @@ newtype TCPPQuery =
9192
TDeadCodePackageQuery(DeadCodeQuery q) or
9293
TDeadCode3PackageQuery(DeadCode3Query q) or
9394
TDeadCode4PackageQuery(DeadCode4Query q) or
95+
TDeadCode9PackageQuery(DeadCode9Query q) or
9496
TDeclarationsPackageQuery(DeclarationsQuery q) or
9597
TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or
9698
TExceptions1PackageQuery(Exceptions1Query q) or
@@ -163,6 +165,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
163165
isDeadCodeQueryMetadata(query, queryId, ruleId, category) or
164166
isDeadCode3QueryMetadata(query, queryId, ruleId, category) or
165167
isDeadCode4QueryMetadata(query, queryId, ruleId, category) or
168+
isDeadCode9QueryMetadata(query, queryId, ruleId, category) or
166169
isDeclarationsQueryMetadata(query, queryId, ruleId, category) or
167170
isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or
168171
isExceptions1QueryMetadata(query, queryId, ruleId, category) or

cpp/common/src/codingstandards/cpp/types/Uses.qll

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
*/
1313

1414
import cpp
15+
import codingstandards.cpp.ast.HiddenFriend
1516

1617
/**
1718
* Gets a typedef with the same qualified name and declared at the same location.
@@ -35,7 +36,8 @@ private TypedefType getAnEquivalentTypeDef(TypedefType type) {
3536
* is from within the function signature or field declaration of the type itself.
3637
*/
3738
Locatable getATypeUse(Type type) {
38-
result = getATypeUse_i(type, _)
39+
result = getATypeUse_i(type, _) and
40+
not isWithinTypeDefinition(result, type)
3941
or
4042
// Identify `TypeMention`s of typedef types, where the underlying type is used.
4143
//
@@ -65,6 +67,39 @@ Locatable getATypeUse(Type type) {
6567
)
6668
}
6769

70+
predicate isWithinTypeDefinition(Locatable loc, Type type) {
71+
loc = type
72+
or
73+
loc.getEnclosingElement*() = type
74+
or
75+
isWithinTypeDefinition(loc.(Function).getDeclaringType(), type)
76+
or
77+
isWithinTypeDefinition(loc.(MemberVariable).getDeclaringType(), type)
78+
or
79+
isWithinTypeDefinition(loc.(PossibleHiddenFriend).getFriendClass(), type)
80+
or
81+
isWithinTypeDefinition(loc.(Parameter).getFunction(), type)
82+
or
83+
isWithinTypeDefinition(loc.(Expr).getEnclosingFunction(), type)
84+
or
85+
isWithinTypeDefinition(loc.(LocalVariable).getFunction(), type)
86+
or
87+
exists(TemplateClass tpl, ClassTemplateSpecialization spec |
88+
tpl = type and
89+
tpl = spec.getPrimaryTemplate() and
90+
isWithinTypeDefinition(loc, spec)
91+
)
92+
//exists(TemplateClass tpl, ClassTemplateSpecialization spec |
93+
// tpl.getAnInstantiation() = type and
94+
// tpl = spec.getPrimaryTemplate() and
95+
// isWithinTypeDefinition(loc, spec)
96+
//)
97+
//exists(ClassTemplateSpecialization spec |
98+
// specializationSharesType(type, spec) and
99+
// isWithinTypeDefinition(loc, spec)
100+
//)
101+
}
102+
68103
private Locatable getATypeUse_i(Type type, string reason) {
69104
(
70105
// Restrict to uses within the source checkout root
@@ -81,6 +116,7 @@ private Locatable getATypeUse_i(Type type, string reason) {
81116
type = v.getType() and
82117
// Ignore self referential variables and parameters
83118
not v.getDeclaringType().refersTo(type) and
119+
not type.(UserType).isAnonymous() and
84120
not type = v.(Parameter).getFunction().getDeclaringType()
85121
) and
86122
reason = "used as a variable type"

cpp/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@
44
| test.cpp:77:11:77:11 | R | Type declaration R is not used. |
55
| test.cpp:90:12:90:12 | (unnamed class/struct/union) | Type declaration (unnamed class/struct/union) is not used. |
66
| test.cpp:111:29:111:30 | AA | Type declaration AA is not used. |
7+
| test.cpp:126:7:126:12 | Nested | Type declaration Nested is not used. |
8+
| test.cpp:135:9:135:20 | UnusedNested | Type declaration UnusedNested is not used. |
9+
| test.cpp:138:7:138:22 | NestedBlockScope | Type declaration NestedBlockScope is not used. |
10+
| test.cpp:149:11:149:16 | Unused | Type declaration Unused is not used. |

cpp/common/test/rules/unusedtypedeclarations/test.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,31 @@ void test_temporary_object_creation() {
121121

122122
return C1{p1};
123123
};
124-
}
124+
}
125+
126+
class Nested { // NON_COMPLIANT - only used within itself
127+
public:
128+
class NestedNested { // COMPLIANT - used by class `Nested`
129+
public:
130+
Nested f();
131+
};
132+
133+
NestedNested g();
134+
135+
class UnusedNested {}; // NON_COMPLIANT - never used by class `Nested`
136+
};
137+
138+
class NestedBlockScope { // NON_COMPLIANT - only used within itself
139+
public:
140+
void f() {
141+
class NestedFunction { // COMPLIANT - used in function below
142+
void g() {
143+
NestedBlockScope l1; // Doesn't count as use of `NestedBlockScope`.
144+
}
145+
};
146+
147+
NestedFunction l1;
148+
149+
class Unused {}; // NON_COMPLIANT - never used by function `f`
150+
}
151+
};

0 commit comments

Comments
 (0)