1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "ScopeChecker.h"
6 #include "CustomMatchers.h"
8 void ScopeChecker::registerMatchers(MatchFinder
*AstMatcher
) {
9 AstMatcher
->addMatcher(varDecl().bind("node"), this);
10 AstMatcher
->addMatcher(cxxNewExpr().bind("node"), this);
11 AstMatcher
->addMatcher(
12 materializeTemporaryExpr(
13 unless(hasDescendant(cxxConstructExpr(allowsTemporary()))))
16 AstMatcher
->addMatcher(
17 callExpr(callee(functionDecl(heapAllocator()))).bind("node"), this);
20 // These enum variants determine whether an allocation has occured in the code.
21 enum AllocationVariety
{
29 // XXX Currently the Decl* in the AutomaticTemporaryMap is unused, but it
30 // probably will be used at some point in the future, in order to produce better
32 typedef DenseMap
<const MaterializeTemporaryExpr
*, const Decl
*>
33 AutomaticTemporaryMap
;
34 AutomaticTemporaryMap AutomaticTemporaries
;
36 void ScopeChecker::check(const MatchFinder::MatchResult
&Result
) {
37 // There are a variety of different reasons why something could be allocated
38 AllocationVariety Variety
= AV_None
;
41 bool IsStaticLocal
= false;
43 if (const ParmVarDecl
*D
= Result
.Nodes
.getNodeAs
<ParmVarDecl
>("node")) {
44 if (D
->hasUnparsedDefaultArg() || D
->hasUninstantiatedDefaultArg()) {
47 if (const Expr
*Default
= D
->getDefaultArg()) {
48 if (const MaterializeTemporaryExpr
*E
=
49 dyn_cast
<MaterializeTemporaryExpr
>(Default
)) {
50 // We have just found a ParmVarDecl which has, as its default argument,
51 // a MaterializeTemporaryExpr. We mark that MaterializeTemporaryExpr as
52 // automatic, by adding it to the AutomaticTemporaryMap.
53 // Reporting on this type will occur when the MaterializeTemporaryExpr
54 // is matched against.
55 AutomaticTemporaries
[E
] = D
;
61 // Determine the type of allocation which we detected
62 if (const VarDecl
*D
= Result
.Nodes
.getNodeAs
<VarDecl
>("node")) {
63 if (D
->hasGlobalStorage()) {
66 Variety
= AV_Automatic
;
69 Loc
= D
->getBeginLoc();
70 IsStaticLocal
= D
->isStaticLocal();
71 } else if (const CXXNewExpr
*E
= Result
.Nodes
.getNodeAs
<CXXNewExpr
>("node")) {
72 // New allocates things on the heap.
73 // We don't consider placement new to do anything, as it doesn't actually
74 // allocate the storage, and thus gives us no useful information.
75 if (!isPlacementNew(E
)) {
77 T
= E
->getAllocatedType();
78 Loc
= E
->getBeginLoc();
80 } else if (const MaterializeTemporaryExpr
*E
=
81 Result
.Nodes
.getNodeAs
<MaterializeTemporaryExpr
>("node")) {
82 // Temporaries can actually have varying storage durations, due to temporary
83 // lifetime extension. We consider the allocation variety of this temporary
84 // to be the same as the allocation variety of its lifetime.
86 // XXX We maybe should mark these lifetimes as being due to a temporary
87 // which has had its lifetime extended, to improve the error messages.
88 switch (E
->getStorageDuration()) {
89 case SD_FullExpression
: {
90 // Check if this temporary is allocated as a default argument!
91 // if it is, we want to pretend that it is automatic.
92 AutomaticTemporaryMap::iterator AutomaticTemporary
=
93 AutomaticTemporaries
.find(E
);
94 if (AutomaticTemporary
!= AutomaticTemporaries
.end()) {
95 Variety
= AV_Automatic
;
97 Variety
= AV_Temporary
;
101 Variety
= AV_Automatic
;
108 assert(false && "I don't think that this ever should occur...");
112 T
= E
->getType().getUnqualifiedType();
113 Loc
= E
->getBeginLoc();
114 } else if (const CallExpr
*E
= Result
.Nodes
.getNodeAs
<CallExpr
>("node")) {
115 T
= E
->getType()->getPointeeType();
117 // This will always allocate on the heap, as the heapAllocator() check
118 // was made in the matcher
120 Loc
= E
->getBeginLoc();
124 // Error messages for incorrect allocations.
125 const char *Stack
= "variable of type %0 only valid on the stack";
126 const char *Global
= "variable of type %0 only valid as global";
127 const char *Heap
= "variable of type %0 only valid on the heap";
128 const char *NonHeap
= "variable of type %0 is not valid on the heap";
129 const char *NonTemporary
= "variable of type %0 is not valid in a temporary";
130 const char *Temporary
= "variable of type %0 is only valid as a temporary";
131 const char *StaticLocal
= "variable of type %0 is only valid as a static "
134 const char *StackNote
=
135 "value incorrectly allocated in an automatic variable";
136 const char *GlobalNote
= "value incorrectly allocated in a global variable";
137 const char *HeapNote
= "value incorrectly allocated on the heap";
138 const char *TemporaryNote
= "value incorrectly allocated in a temporary";
140 // Report errors depending on the annotations on the input types.
146 StackClass
.reportErrorIfPresent(*this, T
, Loc
, Stack
, GlobalNote
);
147 HeapClass
.reportErrorIfPresent(*this, T
, Loc
, Heap
, GlobalNote
);
148 TemporaryClass
.reportErrorIfPresent(*this, T
, Loc
, Temporary
, GlobalNote
);
149 if (!IsStaticLocal
) {
150 StaticLocalClass
.reportErrorIfPresent(*this, T
, Loc
, StaticLocal
,
156 GlobalClass
.reportErrorIfPresent(*this, T
, Loc
, Global
, StackNote
);
157 HeapClass
.reportErrorIfPresent(*this, T
, Loc
, Heap
, StackNote
);
158 TemporaryClass
.reportErrorIfPresent(*this, T
, Loc
, Temporary
, StackNote
);
159 StaticLocalClass
.reportErrorIfPresent(*this, T
, Loc
, StaticLocal
,
164 GlobalClass
.reportErrorIfPresent(*this, T
, Loc
, Global
, TemporaryNote
);
165 HeapClass
.reportErrorIfPresent(*this, T
, Loc
, Heap
, TemporaryNote
);
166 NonTemporaryClass
.reportErrorIfPresent(*this, T
, Loc
, NonTemporary
,
168 StaticLocalClass
.reportErrorIfPresent(*this, T
, Loc
, StaticLocal
,
173 GlobalClass
.reportErrorIfPresent(*this, T
, Loc
, Global
, HeapNote
);
174 StackClass
.reportErrorIfPresent(*this, T
, Loc
, Stack
, HeapNote
);
175 NonHeapClass
.reportErrorIfPresent(*this, T
, Loc
, NonHeap
, HeapNote
);
176 TemporaryClass
.reportErrorIfPresent(*this, T
, Loc
, Temporary
, HeapNote
);
177 StaticLocalClass
.reportErrorIfPresent(*this, T
, Loc
, StaticLocal
, HeapNote
);