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/. */
4 
5 #include "ScopeChecker.h"
6 #include "CustomMatchers.h"
7 
registerMatchers(MatchFinder * AstMatcher)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()))))
14           .bind("node"),
15       this);
16   AstMatcher->addMatcher(
17       callExpr(callee(functionDecl(heapAllocator()))).bind("node"), this);
18 }
19 
20 // These enum variants determine whether an allocation has occured in the code.
21 enum AllocationVariety {
22   AV_None,
23   AV_Global,
24   AV_Automatic,
25   AV_Temporary,
26   AV_Heap,
27 };
28 
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
31 // error messages.
32 typedef DenseMap<const MaterializeTemporaryExpr *, const Decl *>
33     AutomaticTemporaryMap;
34 AutomaticTemporaryMap AutomaticTemporaries;
35 
check(const MatchFinder::MatchResult & Result)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;
39   SourceLocation Loc;
40   QualType T;
41   bool IsStaticLocal = false;
42 
43   if (const ParmVarDecl *D = Result.Nodes.getNodeAs<ParmVarDecl>("node")) {
44     if (D->hasUnparsedDefaultArg() || D->hasUninstantiatedDefaultArg()) {
45       return;
46     }
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;
56       }
57     }
58     return;
59   }
60 
61   // Determine the type of allocation which we detected
62   if (const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("node")) {
63     if (D->hasGlobalStorage()) {
64       Variety = AV_Global;
65     } else {
66       Variety = AV_Automatic;
67     }
68     T = D->getType();
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)) {
76       Variety = AV_Heap;
77       T = E->getAllocatedType();
78       Loc = E->getBeginLoc();
79     }
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.
85 
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;
96       } else {
97         Variety = AV_Temporary;
98       }
99     } break;
100     case SD_Automatic:
101       Variety = AV_Automatic;
102       break;
103     case SD_Thread:
104     case SD_Static:
105       Variety = AV_Global;
106       break;
107     case SD_Dynamic:
108       assert(false && "I don't think that this ever should occur...");
109       Variety = AV_Heap;
110       break;
111     }
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();
116     if (!T.isNull()) {
117       // This will always allocate on the heap, as the heapAllocator() check
118       // was made in the matcher
119       Variety = AV_Heap;
120       Loc = E->getBeginLoc();
121     }
122   }
123 
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 "
132                             "local";
133 
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";
139 
140   // Report errors depending on the annotations on the input types.
141   switch (Variety) {
142   case AV_None:
143     return;
144 
145   case AV_Global:
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,
151                                             GlobalNote);
152     }
153     break;
154 
155   case AV_Automatic:
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,
160                                           StackNote);
161     break;
162 
163   case AV_Temporary:
164     GlobalClass.reportErrorIfPresent(*this, T, Loc, Global, TemporaryNote);
165     HeapClass.reportErrorIfPresent(*this, T, Loc, Heap, TemporaryNote);
166     NonTemporaryClass.reportErrorIfPresent(*this, T, Loc, NonTemporary,
167                                            TemporaryNote);
168     StaticLocalClass.reportErrorIfPresent(*this, T, Loc, StaticLocal,
169                                           TemporaryNote);
170     break;
171 
172   case AV_Heap:
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);
178     break;
179   }
180 }
181