xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===-- ContainerModeling.cpp -------------------------------------*- C++ -*--//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Defines a modeling-checker for modeling STL container-like containers.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "clang/AST/DeclTemplate.h"
14 #include "clang/Driver/DriverDiagnostic.h"
15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17 #include "clang/StaticAnalyzer/Core/Checker.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
22 
23 #include "Iterator.h"
24 
25 #include <utility>
26 
27 using namespace clang;
28 using namespace ento;
29 using namespace iterator;
30 
31 namespace {
32 
33 class ContainerModeling
34   : public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> {
35 
36   void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal,
37                    SVal Cont) const;
38   void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal,
39                  SVal Cont) const;
40   void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr,
41                         SVal OldCont = UndefinedVal()) const;
42   void handleAssign(CheckerContext &C, SVal Cont, const Expr *ContE) const;
43   void handleClear(CheckerContext &C, SVal Cont, const Expr *ContE) const;
44   void handlePushBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;
45   void handlePopBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;
46   void handlePushFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;
47   void handlePopFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;
48   void handleInsert(CheckerContext &C, SVal Cont, SVal Iter) const;
49   void handleErase(CheckerContext &C, SVal Cont, SVal Iter) const;
50   void handleErase(CheckerContext &C, SVal Cont, SVal Iter1, SVal Iter2) const;
51   void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter) const;
52   void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter1,
53                         SVal Iter2) const;
54   const NoteTag *getChangeTag(CheckerContext &C, StringRef Text,
55                               const MemRegion *ContReg,
56                               const Expr *ContE) const;
57   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
58                   const char *Sep) const override;
59 
60 public:
61   ContainerModeling() = default;
62 
63   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
64   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
65   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
66 
67   using NoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,
68                                                   const Expr *) const;
69   using OneItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,
70                                                    SVal) const;
71   using TwoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, SVal,
72                                                    SVal) const;
73 
74   CallDescriptionMap<NoItParamFn> NoIterParamFunctions = {
75       {{CDM::CXXMethod, {"clear"}, 0}, &ContainerModeling::handleClear},
76       {{CDM::CXXMethod, {"assign"}, 2}, &ContainerModeling::handleAssign},
77       {{CDM::CXXMethod, {"push_back"}, 1}, &ContainerModeling::handlePushBack},
78       {{CDM::CXXMethod, {"emplace_back"}, 1},
79        &ContainerModeling::handlePushBack},
80       {{CDM::CXXMethod, {"pop_back"}, 0}, &ContainerModeling::handlePopBack},
81       {{CDM::CXXMethod, {"push_front"}, 1},
82        &ContainerModeling::handlePushFront},
83       {{CDM::CXXMethod, {"emplace_front"}, 1},
84        &ContainerModeling::handlePushFront},
85       {{CDM::CXXMethod, {"pop_front"}, 0}, &ContainerModeling::handlePopFront},
86   };
87 
88   CallDescriptionMap<OneItParamFn> OneIterParamFunctions = {
89       {{CDM::CXXMethod, {"insert"}, 2}, &ContainerModeling::handleInsert},
90       {{CDM::CXXMethod, {"emplace"}, 2}, &ContainerModeling::handleInsert},
91       {{CDM::CXXMethod, {"erase"}, 1}, &ContainerModeling::handleErase},
92       {{CDM::CXXMethod, {"erase_after"}, 1},
93        &ContainerModeling::handleEraseAfter},
94   };
95 
96   CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = {
97       {{CDM::CXXMethod, {"erase"}, 2}, &ContainerModeling::handleErase},
98       {{CDM::CXXMethod, {"erase_after"}, 2},
99        &ContainerModeling::handleEraseAfter},
100   };
101 };
102 
103 bool isBeginCall(const FunctionDecl *Func);
104 bool isEndCall(const FunctionDecl *Func);
105 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
106 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
107 bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
108 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
109 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
110 ProgramStateRef createContainerBegin(ProgramStateRef State,
111                                      const MemRegion *Cont, const Expr *E,
112                                      QualType T, const LocationContext *LCtx,
113                                      unsigned BlockCount);
114 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
115                                    const Expr *E, QualType T,
116                                    const LocationContext *LCtx,
117                                    unsigned BlockCount);
118 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
119                                  const ContainerData &CData);
120 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
121                                                const MemRegion *Cont);
122 ProgramStateRef
123 invalidateAllIteratorPositionsExcept(ProgramStateRef State,
124                                      const MemRegion *Cont, SymbolRef Offset,
125                                      BinaryOperator::Opcode Opc);
126 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
127                                             SymbolRef Offset,
128                                             BinaryOperator::Opcode Opc);
129 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
130                                             SymbolRef Offset1,
131                                             BinaryOperator::Opcode Opc1,
132                                             SymbolRef Offset2,
133                                             BinaryOperator::Opcode Opc2);
134 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
135                                              const MemRegion *Cont,
136                                              const MemRegion *NewCont);
137 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
138                                                    const MemRegion *Cont,
139                                                    const MemRegion *NewCont,
140                                                    SymbolRef Offset,
141                                                    BinaryOperator::Opcode Opc);
142 ProgramStateRef rebaseSymbolInIteratorPositionsIf(
143     ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
144     SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
145 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
146                         SymbolRef OldSym, SymbolRef NewSym);
147 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
148 
149 } // namespace
150 
checkPostCall(const CallEvent & Call,CheckerContext & C) const151 void ContainerModeling::checkPostCall(const CallEvent &Call,
152                                      CheckerContext &C) const {
153   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
154   if (!Func)
155     return;
156 
157   if (Func->isOverloadedOperator()) {
158     const auto Op = Func->getOverloadedOperator();
159     if (Op == OO_Equal) {
160       // Overloaded 'operator=' must be a non-static member function.
161       const auto *InstCall = cast<CXXInstanceCall>(&Call);
162       if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
163         handleAssignment(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
164                      Call.getArgSVal(0));
165         return;
166       }
167 
168       handleAssignment(C, InstCall->getCXXThisVal());
169       return;
170     }
171   } else {
172     if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
173       const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call);
174       if (Handler0) {
175         (this->**Handler0)(C, InstCall->getCXXThisVal(),
176                            InstCall->getCXXThisExpr());
177         return;
178       }
179 
180       const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call);
181       if (Handler1) {
182         (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
183         return;
184       }
185 
186       const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call);
187       if (Handler2) {
188         (this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0),
189                            Call.getArgSVal(1));
190         return;
191       }
192 
193       const auto *OrigExpr = Call.getOriginExpr();
194       if (!OrigExpr)
195         return;
196 
197       if (isBeginCall(Func)) {
198         handleBegin(C, OrigExpr, Call.getReturnValue(),
199                     InstCall->getCXXThisVal());
200         return;
201       }
202 
203       if (isEndCall(Func)) {
204         handleEnd(C, OrigExpr, Call.getReturnValue(),
205                   InstCall->getCXXThisVal());
206         return;
207       }
208     }
209   }
210 }
211 
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const212 void ContainerModeling::checkLiveSymbols(ProgramStateRef State,
213                                          SymbolReaper &SR) const {
214   // Keep symbolic expressions of container begins and ends alive
215   auto ContMap = State->get<ContainerMap>();
216   for (const auto &Cont : ContMap) {
217     const auto CData = Cont.second;
218     if (CData.getBegin()) {
219       SR.markLive(CData.getBegin());
220       if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
221         SR.markLive(SIE->getLHS());
222     }
223     if (CData.getEnd()) {
224       SR.markLive(CData.getEnd());
225       if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
226         SR.markLive(SIE->getLHS());
227     }
228   }
229 }
230 
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const231 void ContainerModeling::checkDeadSymbols(SymbolReaper &SR,
232                                          CheckerContext &C) const {
233   // Cleanup
234   auto State = C.getState();
235 
236   auto ContMap = State->get<ContainerMap>();
237   for (const auto &Cont : ContMap) {
238     if (!SR.isLiveRegion(Cont.first)) {
239       // We must keep the container data while it has live iterators to be able
240       // to compare them to the begin and the end of the container.
241       if (!hasLiveIterators(State, Cont.first)) {
242         State = State->remove<ContainerMap>(Cont.first);
243       }
244     }
245   }
246 
247   C.addTransition(State);
248 }
249 
handleBegin(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Cont) const250 void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE,
251                                    SVal RetVal, SVal Cont) const {
252   const auto *ContReg = Cont.getAsRegion();
253   if (!ContReg)
254     return;
255 
256   ContReg = ContReg->getMostDerivedObjectRegion();
257 
258   // If the container already has a begin symbol then use it. Otherwise first
259   // create a new one.
260   auto State = C.getState();
261   auto BeginSym = getContainerBegin(State, ContReg);
262   if (!BeginSym) {
263     State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
264                                  C.getLocationContext(), C.blockCount());
265     BeginSym = getContainerBegin(State, ContReg);
266   }
267   State = setIteratorPosition(State, RetVal,
268                               IteratorPosition::getPosition(ContReg, BeginSym));
269   C.addTransition(State);
270 }
271 
handleEnd(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Cont) const272 void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE,
273                                  SVal RetVal, SVal Cont) const {
274   const auto *ContReg = Cont.getAsRegion();
275   if (!ContReg)
276     return;
277 
278   ContReg = ContReg->getMostDerivedObjectRegion();
279 
280   // If the container already has an end symbol then use it. Otherwise first
281   // create a new one.
282   auto State = C.getState();
283   auto EndSym = getContainerEnd(State, ContReg);
284   if (!EndSym) {
285     State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
286                                C.getLocationContext(), C.blockCount());
287     EndSym = getContainerEnd(State, ContReg);
288   }
289   State = setIteratorPosition(State, RetVal,
290                               IteratorPosition::getPosition(ContReg, EndSym));
291   C.addTransition(State);
292 }
293 
handleAssignment(CheckerContext & C,SVal Cont,const Expr * CE,SVal OldCont) const294 void ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont,
295                                          const Expr *CE, SVal OldCont) const {
296   const auto *ContReg = Cont.getAsRegion();
297   if (!ContReg)
298     return;
299 
300   ContReg = ContReg->getMostDerivedObjectRegion();
301 
302   // Assignment of a new value to a container always invalidates all its
303   // iterators
304   auto State = C.getState();
305   const auto CData = getContainerData(State, ContReg);
306   if (CData) {
307     State = invalidateAllIteratorPositions(State, ContReg);
308   }
309 
310   // In case of move, iterators of the old container (except the past-end
311   // iterators) remain valid but refer to the new container
312   if (!OldCont.isUndef()) {
313     const auto *OldContReg = OldCont.getAsRegion();
314     if (OldContReg) {
315       OldContReg = OldContReg->getMostDerivedObjectRegion();
316       const auto OldCData = getContainerData(State, OldContReg);
317       if (OldCData) {
318         if (const auto OldEndSym = OldCData->getEnd()) {
319           // If we already assigned an "end" symbol to the old container, then
320           // first reassign all iterator positions to the new container which
321           // are not past the container (thus not greater or equal to the
322           // current "end" symbol).
323           State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
324                                                      OldEndSym, BO_GE);
325           auto &SymMgr = C.getSymbolManager();
326           auto &SVB = C.getSValBuilder();
327           // Then generate and assign a new "end" symbol for the new container.
328           auto NewEndSym =
329               SymMgr.conjureSymbol(CE, C.getLocationContext(),
330                                    C.getASTContext().LongTy, C.blockCount());
331           State = assumeNoOverflow(State, NewEndSym, 4);
332           if (CData) {
333             State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
334           } else {
335             State = setContainerData(State, ContReg,
336                                      ContainerData::fromEnd(NewEndSym));
337           }
338           // Finally, replace the old "end" symbol in the already reassigned
339           // iterator positions with the new "end" symbol.
340           State = rebaseSymbolInIteratorPositionsIf(
341               State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
342         } else {
343           // There was no "end" symbol assigned yet to the old container,
344           // so reassign all iterator positions to the new container.
345           State = reassignAllIteratorPositions(State, OldContReg, ContReg);
346         }
347         if (const auto OldBeginSym = OldCData->getBegin()) {
348           // If we already assigned a "begin" symbol to the old container, then
349           // assign it to the new container and remove it from the old one.
350           if (CData) {
351             State =
352                 setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
353           } else {
354             State = setContainerData(State, ContReg,
355                                      ContainerData::fromBegin(OldBeginSym));
356           }
357           State =
358               setContainerData(State, OldContReg, OldCData->newBegin(nullptr));
359         }
360       } else {
361         // There was neither "begin" nor "end" symbol assigned yet to the old
362         // container, so reassign all iterator positions to the new container.
363         State = reassignAllIteratorPositions(State, OldContReg, ContReg);
364       }
365     }
366   }
367   C.addTransition(State);
368 }
369 
handleAssign(CheckerContext & C,SVal Cont,const Expr * ContE) const370 void ContainerModeling::handleAssign(CheckerContext &C, SVal Cont,
371                                      const Expr *ContE) const {
372   const auto *ContReg = Cont.getAsRegion();
373   if (!ContReg)
374     return;
375 
376   ContReg = ContReg->getMostDerivedObjectRegion();
377 
378   // The assign() operation invalidates all the iterators
379   auto State = C.getState();
380   State = invalidateAllIteratorPositions(State, ContReg);
381   C.addTransition(State);
382 }
383 
handleClear(CheckerContext & C,SVal Cont,const Expr * ContE) const384 void ContainerModeling::handleClear(CheckerContext &C, SVal Cont,
385                                     const Expr *ContE) const {
386   const auto *ContReg = Cont.getAsRegion();
387   if (!ContReg)
388     return;
389 
390   ContReg = ContReg->getMostDerivedObjectRegion();
391 
392   // The clear() operation invalidates all the iterators, except the past-end
393   // iterators of list-like containers
394   auto State = C.getState();
395   if (!hasSubscriptOperator(State, ContReg) ||
396       !backModifiable(State, ContReg)) {
397     const auto CData = getContainerData(State, ContReg);
398     if (CData) {
399       if (const auto EndSym = CData->getEnd()) {
400         State =
401             invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
402         C.addTransition(State);
403         return;
404       }
405     }
406   }
407   const NoteTag *ChangeTag =
408     getChangeTag(C, "became empty", ContReg, ContE);
409   State = invalidateAllIteratorPositions(State, ContReg);
410   C.addTransition(State, ChangeTag);
411 }
412 
handlePushBack(CheckerContext & C,SVal Cont,const Expr * ContE) const413 void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont,
414                                        const Expr *ContE) const {
415   const auto *ContReg = Cont.getAsRegion();
416   if (!ContReg)
417     return;
418 
419   ContReg = ContReg->getMostDerivedObjectRegion();
420 
421   // For deque-like containers invalidate all iterator positions
422   auto State = C.getState();
423   if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
424     State = invalidateAllIteratorPositions(State, ContReg);
425     C.addTransition(State);
426     return;
427   }
428 
429   const auto CData = getContainerData(State, ContReg);
430   if (!CData)
431     return;
432 
433   // For vector-like containers invalidate the past-end iterator positions
434   if (const auto EndSym = CData->getEnd()) {
435     if (hasSubscriptOperator(State, ContReg)) {
436       State = invalidateIteratorPositions(State, EndSym, BO_GE);
437     }
438     auto &SymMgr = C.getSymbolManager();
439     auto &BVF = SymMgr.getBasicVals();
440     auto &SVB = C.getSValBuilder();
441     const auto newEndSym =
442       SVB.evalBinOp(State, BO_Add,
443                     nonloc::SymbolVal(EndSym),
444                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
445                     SymMgr.getType(EndSym)).getAsSymbol();
446     const NoteTag *ChangeTag =
447       getChangeTag(C, "extended to the back by 1 position", ContReg, ContE);
448     State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
449     C.addTransition(State, ChangeTag);
450   }
451 }
452 
handlePopBack(CheckerContext & C,SVal Cont,const Expr * ContE) const453 void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont,
454                                       const Expr *ContE) const {
455   const auto *ContReg = Cont.getAsRegion();
456   if (!ContReg)
457     return;
458 
459   ContReg = ContReg->getMostDerivedObjectRegion();
460 
461   auto State = C.getState();
462   const auto CData = getContainerData(State, ContReg);
463   if (!CData)
464     return;
465 
466   if (const auto EndSym = CData->getEnd()) {
467     auto &SymMgr = C.getSymbolManager();
468     auto &BVF = SymMgr.getBasicVals();
469     auto &SVB = C.getSValBuilder();
470     const auto BackSym =
471       SVB.evalBinOp(State, BO_Sub,
472                     nonloc::SymbolVal(EndSym),
473                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
474                     SymMgr.getType(EndSym)).getAsSymbol();
475     const NoteTag *ChangeTag =
476       getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE);
477     // For vector-like and deque-like containers invalidate the last and the
478     // past-end iterator positions. For list-like containers only invalidate
479     // the last position
480     if (hasSubscriptOperator(State, ContReg) &&
481         backModifiable(State, ContReg)) {
482       State = invalidateIteratorPositions(State, BackSym, BO_GE);
483       State = setContainerData(State, ContReg, CData->newEnd(nullptr));
484     } else {
485       State = invalidateIteratorPositions(State, BackSym, BO_EQ);
486     }
487     auto newEndSym = BackSym;
488     State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
489     C.addTransition(State, ChangeTag);
490   }
491 }
492 
handlePushFront(CheckerContext & C,SVal Cont,const Expr * ContE) const493 void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont,
494                                         const Expr *ContE) const {
495   const auto *ContReg = Cont.getAsRegion();
496   if (!ContReg)
497     return;
498 
499   ContReg = ContReg->getMostDerivedObjectRegion();
500 
501   // For deque-like containers invalidate all iterator positions
502   auto State = C.getState();
503   if (hasSubscriptOperator(State, ContReg)) {
504     State = invalidateAllIteratorPositions(State, ContReg);
505     C.addTransition(State);
506   } else {
507     const auto CData = getContainerData(State, ContReg);
508     if (!CData)
509       return;
510 
511     if (const auto BeginSym = CData->getBegin()) {
512       auto &SymMgr = C.getSymbolManager();
513       auto &BVF = SymMgr.getBasicVals();
514       auto &SVB = C.getSValBuilder();
515       const auto newBeginSym =
516         SVB.evalBinOp(State, BO_Sub,
517                       nonloc::SymbolVal(BeginSym),
518                       nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
519                       SymMgr.getType(BeginSym)).getAsSymbol();
520       const NoteTag *ChangeTag =
521         getChangeTag(C, "extended to the front by 1 position", ContReg, ContE);
522       State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
523       C.addTransition(State, ChangeTag);
524     }
525   }
526 }
527 
handlePopFront(CheckerContext & C,SVal Cont,const Expr * ContE) const528 void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont,
529                                        const Expr *ContE) const {
530   const auto *ContReg = Cont.getAsRegion();
531   if (!ContReg)
532     return;
533 
534   ContReg = ContReg->getMostDerivedObjectRegion();
535 
536   auto State = C.getState();
537   const auto CData = getContainerData(State, ContReg);
538   if (!CData)
539     return;
540 
541   // For deque-like containers invalidate all iterator positions. For list-like
542   // iterators only invalidate the first position
543   if (const auto BeginSym = CData->getBegin()) {
544     if (hasSubscriptOperator(State, ContReg)) {
545       State = invalidateIteratorPositions(State, BeginSym, BO_LE);
546     } else {
547       State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
548     }
549     auto &SymMgr = C.getSymbolManager();
550     auto &BVF = SymMgr.getBasicVals();
551     auto &SVB = C.getSValBuilder();
552     const auto newBeginSym =
553       SVB.evalBinOp(State, BO_Add,
554                     nonloc::SymbolVal(BeginSym),
555                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
556                     SymMgr.getType(BeginSym)).getAsSymbol();
557     const NoteTag *ChangeTag =
558       getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE);
559     State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
560     C.addTransition(State, ChangeTag);
561   }
562 }
563 
handleInsert(CheckerContext & C,SVal Cont,SVal Iter) const564 void ContainerModeling::handleInsert(CheckerContext &C, SVal Cont,
565                                      SVal Iter) const {
566   const auto *ContReg = Cont.getAsRegion();
567   if (!ContReg)
568     return;
569 
570   ContReg = ContReg->getMostDerivedObjectRegion();
571 
572   auto State = C.getState();
573   const auto *Pos = getIteratorPosition(State, Iter);
574   if (!Pos)
575     return;
576 
577   // For deque-like containers invalidate all iterator positions. For
578   // vector-like containers invalidate iterator positions after the insertion.
579   if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
580     if (frontModifiable(State, ContReg)) {
581       State = invalidateAllIteratorPositions(State, ContReg);
582     } else {
583       State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
584     }
585     if (const auto *CData = getContainerData(State, ContReg)) {
586       if (const auto EndSym = CData->getEnd()) {
587         State = invalidateIteratorPositions(State, EndSym, BO_GE);
588         State = setContainerData(State, ContReg, CData->newEnd(nullptr));
589       }
590     }
591     C.addTransition(State);
592   }
593 }
594 
handleErase(CheckerContext & C,SVal Cont,SVal Iter) const595 void ContainerModeling::handleErase(CheckerContext &C, SVal Cont,
596                                     SVal Iter) const {
597   const auto *ContReg = Cont.getAsRegion();
598   if (!ContReg)
599     return;
600 
601   ContReg = ContReg->getMostDerivedObjectRegion();
602 
603   auto State = C.getState();
604   const auto *Pos = getIteratorPosition(State, Iter);
605   if (!Pos)
606     return;
607 
608   // For deque-like containers invalidate all iterator positions. For
609   // vector-like containers invalidate iterator positions at and after the
610   // deletion. For list-like containers only invalidate the deleted position.
611   if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
612     if (frontModifiable(State, ContReg)) {
613       State = invalidateAllIteratorPositions(State, ContReg);
614     } else {
615       State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
616     }
617     if (const auto *CData = getContainerData(State, ContReg)) {
618       if (const auto EndSym = CData->getEnd()) {
619         State = invalidateIteratorPositions(State, EndSym, BO_GE);
620         State = setContainerData(State, ContReg, CData->newEnd(nullptr));
621       }
622     }
623   } else {
624     State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
625   }
626   C.addTransition(State);
627 }
628 
handleErase(CheckerContext & C,SVal Cont,SVal Iter1,SVal Iter2) const629 void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1,
630                                     SVal Iter2) const {
631   const auto *ContReg = Cont.getAsRegion();
632   if (!ContReg)
633     return;
634 
635   ContReg = ContReg->getMostDerivedObjectRegion();
636   auto State = C.getState();
637   const auto *Pos1 = getIteratorPosition(State, Iter1);
638   const auto *Pos2 = getIteratorPosition(State, Iter2);
639   if (!Pos1 || !Pos2)
640     return;
641 
642   // For deque-like containers invalidate all iterator positions. For
643   // vector-like containers invalidate iterator positions at and after the
644   // deletion range. For list-like containers only invalidate the deleted
645   // position range [first..last].
646   if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
647     if (frontModifiable(State, ContReg)) {
648       State = invalidateAllIteratorPositions(State, ContReg);
649     } else {
650       State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
651     }
652     if (const auto *CData = getContainerData(State, ContReg)) {
653       if (const auto EndSym = CData->getEnd()) {
654         State = invalidateIteratorPositions(State, EndSym, BO_GE);
655         State = setContainerData(State, ContReg, CData->newEnd(nullptr));
656       }
657     }
658   } else {
659     State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
660                                         Pos2->getOffset(), BO_LT);
661   }
662   C.addTransition(State);
663 }
664 
handleEraseAfter(CheckerContext & C,SVal Cont,SVal Iter) const665 void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
666                                         SVal Iter) const {
667   auto State = C.getState();
668   const auto *Pos = getIteratorPosition(State, Iter);
669   if (!Pos)
670     return;
671 
672   // Invalidate the deleted iterator position, which is the position of the
673   // parameter plus one.
674   auto &SymMgr = C.getSymbolManager();
675   auto &BVF = SymMgr.getBasicVals();
676   auto &SVB = C.getSValBuilder();
677   const auto NextSym =
678     SVB.evalBinOp(State, BO_Add,
679                   nonloc::SymbolVal(Pos->getOffset()),
680                   nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
681                   SymMgr.getType(Pos->getOffset())).getAsSymbol();
682   State = invalidateIteratorPositions(State, NextSym, BO_EQ);
683   C.addTransition(State);
684 }
685 
handleEraseAfter(CheckerContext & C,SVal Cont,SVal Iter1,SVal Iter2) const686 void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
687                                          SVal Iter1, SVal Iter2) const {
688   auto State = C.getState();
689   const auto *Pos1 = getIteratorPosition(State, Iter1);
690   const auto *Pos2 = getIteratorPosition(State, Iter2);
691   if (!Pos1 || !Pos2)
692     return;
693 
694   // Invalidate the deleted iterator position range (first..last)
695   State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
696                                       Pos2->getOffset(), BO_LT);
697   C.addTransition(State);
698 }
699 
getChangeTag(CheckerContext & C,StringRef Text,const MemRegion * ContReg,const Expr * ContE) const700 const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C,
701                                                StringRef Text,
702                                                const MemRegion *ContReg,
703                                                const Expr *ContE) const {
704   StringRef Name;
705   // First try to get the name of the variable from the region
706   if (const auto *DR = dyn_cast<DeclRegion>(ContReg)) {
707     Name = DR->getDecl()->getName();
708   // If the region is not a `DeclRegion` then use the expression instead
709   } else if (const auto *DRE =
710              dyn_cast<DeclRefExpr>(ContE->IgnoreParenCasts())) {
711     Name = DRE->getDecl()->getName();
712   }
713 
714   return C.getNoteTag(
715       [Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string {
716         if (!BR.isInteresting(ContReg))
717           return "";
718 
719         SmallString<256> Msg;
720         llvm::raw_svector_ostream Out(Msg);
721         Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : "" )
722             << Text;
723         return std::string(Out.str());
724       });
725 }
726 
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const727 void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State,
728                                   const char *NL, const char *Sep) const {
729   auto ContMap = State->get<ContainerMap>();
730 
731   if (!ContMap.isEmpty()) {
732     Out << Sep << "Container Data :" << NL;
733     for (const auto &Cont : ContMap) {
734       Cont.first->dumpToStream(Out);
735       Out << " : [ ";
736       const auto CData = Cont.second;
737       if (CData.getBegin())
738         CData.getBegin()->dumpToStream(Out);
739       else
740         Out << "<Unknown>";
741       Out << " .. ";
742       if (CData.getEnd())
743         CData.getEnd()->dumpToStream(Out);
744       else
745         Out << "<Unknown>";
746       Out << " ]";
747     }
748   }
749 }
750 
751 namespace {
752 
isBeginCall(const FunctionDecl * Func)753 bool isBeginCall(const FunctionDecl *Func) {
754   const auto *IdInfo = Func->getIdentifier();
755   if (!IdInfo)
756     return false;
757   return IdInfo->getName().ends_with_insensitive("begin");
758 }
759 
isEndCall(const FunctionDecl * Func)760 bool isEndCall(const FunctionDecl *Func) {
761   const auto *IdInfo = Func->getIdentifier();
762   if (!IdInfo)
763     return false;
764   return IdInfo->getName().ends_with_insensitive("end");
765 }
766 
getCXXRecordDecl(ProgramStateRef State,const MemRegion * Reg)767 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
768                                       const MemRegion *Reg) {
769   auto TI = getDynamicTypeInfo(State, Reg);
770   if (!TI.isValid())
771     return nullptr;
772 
773   auto Type = TI.getType();
774   if (const auto *RefT = Type->getAs<ReferenceType>()) {
775     Type = RefT->getPointeeType();
776   }
777 
778   if (const auto *PtrT = Type->getAs<PointerType>()) {
779     Type = PtrT->getPointeeType();
780   }
781 
782   return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
783 }
784 
hasSubscriptOperator(ProgramStateRef State,const MemRegion * Reg)785 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
786   const auto *CRD = getCXXRecordDecl(State, Reg);
787   if (!CRD)
788     return false;
789 
790   for (const auto *Method : CRD->methods()) {
791     if (!Method->isOverloadedOperator())
792       continue;
793     const auto OPK = Method->getOverloadedOperator();
794     if (OPK == OO_Subscript) {
795       return true;
796     }
797   }
798   return false;
799 }
800 
frontModifiable(ProgramStateRef State,const MemRegion * Reg)801 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
802   const auto *CRD = getCXXRecordDecl(State, Reg);
803   if (!CRD)
804     return false;
805 
806   for (const auto *Method : CRD->methods()) {
807     if (!Method->getDeclName().isIdentifier())
808       continue;
809     if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
810       return true;
811     }
812   }
813   return false;
814 }
815 
backModifiable(ProgramStateRef State,const MemRegion * Reg)816 bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
817   const auto *CRD = getCXXRecordDecl(State, Reg);
818   if (!CRD)
819     return false;
820 
821   for (const auto *Method : CRD->methods()) {
822     if (!Method->getDeclName().isIdentifier())
823       continue;
824     if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
825       return true;
826     }
827   }
828   return false;
829 }
830 
getContainerBegin(ProgramStateRef State,const MemRegion * Cont)831 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
832   const auto *CDataPtr = getContainerData(State, Cont);
833   if (!CDataPtr)
834     return nullptr;
835 
836   return CDataPtr->getBegin();
837 }
838 
getContainerEnd(ProgramStateRef State,const MemRegion * Cont)839 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
840   const auto *CDataPtr = getContainerData(State, Cont);
841   if (!CDataPtr)
842     return nullptr;
843 
844   return CDataPtr->getEnd();
845 }
846 
createContainerBegin(ProgramStateRef State,const MemRegion * Cont,const Expr * E,QualType T,const LocationContext * LCtx,unsigned BlockCount)847 ProgramStateRef createContainerBegin(ProgramStateRef State,
848                                      const MemRegion *Cont, const Expr *E,
849                                      QualType T, const LocationContext *LCtx,
850                                      unsigned BlockCount) {
851   // Only create if it does not exist
852   const auto *CDataPtr = getContainerData(State, Cont);
853   if (CDataPtr && CDataPtr->getBegin())
854     return State;
855 
856   auto &SymMgr = State->getSymbolManager();
857   const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
858                                                    "begin");
859   State = assumeNoOverflow(State, Sym, 4);
860 
861   if (CDataPtr) {
862     const auto CData = CDataPtr->newBegin(Sym);
863     return setContainerData(State, Cont, CData);
864   }
865 
866   const auto CData = ContainerData::fromBegin(Sym);
867   return setContainerData(State, Cont, CData);
868 }
869 
createContainerEnd(ProgramStateRef State,const MemRegion * Cont,const Expr * E,QualType T,const LocationContext * LCtx,unsigned BlockCount)870 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
871                                    const Expr *E, QualType T,
872                                    const LocationContext *LCtx,
873                                    unsigned BlockCount) {
874   // Only create if it does not exist
875   const auto *CDataPtr = getContainerData(State, Cont);
876   if (CDataPtr && CDataPtr->getEnd())
877     return State;
878 
879   auto &SymMgr = State->getSymbolManager();
880   const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
881                                                   "end");
882   State = assumeNoOverflow(State, Sym, 4);
883 
884   if (CDataPtr) {
885     const auto CData = CDataPtr->newEnd(Sym);
886     return setContainerData(State, Cont, CData);
887   }
888 
889   const auto CData = ContainerData::fromEnd(Sym);
890   return setContainerData(State, Cont, CData);
891 }
892 
setContainerData(ProgramStateRef State,const MemRegion * Cont,const ContainerData & CData)893 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
894                                  const ContainerData &CData) {
895   return State->set<ContainerMap>(Cont, CData);
896 }
897 
898 template <typename Condition, typename Process>
processIteratorPositions(ProgramStateRef State,Condition Cond,Process Proc)899 ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
900                                          Process Proc) {
901   auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
902   auto RegionMap = State->get<IteratorRegionMap>();
903   bool Changed = false;
904   for (const auto &Reg : RegionMap) {
905     if (Cond(Reg.second)) {
906       RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
907       Changed = true;
908     }
909   }
910 
911   if (Changed)
912     State = State->set<IteratorRegionMap>(RegionMap);
913 
914   auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
915   auto SymbolMap = State->get<IteratorSymbolMap>();
916   Changed = false;
917   for (const auto &Sym : SymbolMap) {
918     if (Cond(Sym.second)) {
919       SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
920       Changed = true;
921     }
922   }
923 
924   if (Changed)
925     State = State->set<IteratorSymbolMap>(SymbolMap);
926 
927   return State;
928 }
929 
invalidateAllIteratorPositions(ProgramStateRef State,const MemRegion * Cont)930 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
931                                                const MemRegion *Cont) {
932   auto MatchCont = [&](const IteratorPosition &Pos) {
933     return Pos.getContainer() == Cont;
934   };
935   auto Invalidate = [&](const IteratorPosition &Pos) {
936     return Pos.invalidate();
937   };
938   return processIteratorPositions(State, MatchCont, Invalidate);
939 }
940 
941 ProgramStateRef
invalidateAllIteratorPositionsExcept(ProgramStateRef State,const MemRegion * Cont,SymbolRef Offset,BinaryOperator::Opcode Opc)942 invalidateAllIteratorPositionsExcept(ProgramStateRef State,
943                                      const MemRegion *Cont, SymbolRef Offset,
944                                      BinaryOperator::Opcode Opc) {
945   auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
946     return Pos.getContainer() == Cont &&
947            !compare(State, Pos.getOffset(), Offset, Opc);
948   };
949   auto Invalidate = [&](const IteratorPosition &Pos) {
950     return Pos.invalidate();
951   };
952   return processIteratorPositions(State, MatchContAndCompare, Invalidate);
953 }
954 
invalidateIteratorPositions(ProgramStateRef State,SymbolRef Offset,BinaryOperator::Opcode Opc)955 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
956                                             SymbolRef Offset,
957                                             BinaryOperator::Opcode Opc) {
958   auto Compare = [&](const IteratorPosition &Pos) {
959     return compare(State, Pos.getOffset(), Offset, Opc);
960   };
961   auto Invalidate = [&](const IteratorPosition &Pos) {
962     return Pos.invalidate();
963   };
964   return processIteratorPositions(State, Compare, Invalidate);
965 }
966 
invalidateIteratorPositions(ProgramStateRef State,SymbolRef Offset1,BinaryOperator::Opcode Opc1,SymbolRef Offset2,BinaryOperator::Opcode Opc2)967 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
968                                             SymbolRef Offset1,
969                                             BinaryOperator::Opcode Opc1,
970                                             SymbolRef Offset2,
971                                             BinaryOperator::Opcode Opc2) {
972   auto Compare = [&](const IteratorPosition &Pos) {
973     return compare(State, Pos.getOffset(), Offset1, Opc1) &&
974            compare(State, Pos.getOffset(), Offset2, Opc2);
975   };
976   auto Invalidate = [&](const IteratorPosition &Pos) {
977     return Pos.invalidate();
978   };
979   return processIteratorPositions(State, Compare, Invalidate);
980 }
981 
reassignAllIteratorPositions(ProgramStateRef State,const MemRegion * Cont,const MemRegion * NewCont)982 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
983                                              const MemRegion *Cont,
984                                              const MemRegion *NewCont) {
985   auto MatchCont = [&](const IteratorPosition &Pos) {
986     return Pos.getContainer() == Cont;
987   };
988   auto ReAssign = [&](const IteratorPosition &Pos) {
989     return Pos.reAssign(NewCont);
990   };
991   return processIteratorPositions(State, MatchCont, ReAssign);
992 }
993 
reassignAllIteratorPositionsUnless(ProgramStateRef State,const MemRegion * Cont,const MemRegion * NewCont,SymbolRef Offset,BinaryOperator::Opcode Opc)994 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
995                                                    const MemRegion *Cont,
996                                                    const MemRegion *NewCont,
997                                                    SymbolRef Offset,
998                                                    BinaryOperator::Opcode Opc) {
999   auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
1000     return Pos.getContainer() == Cont &&
1001     !compare(State, Pos.getOffset(), Offset, Opc);
1002   };
1003   auto ReAssign = [&](const IteratorPosition &Pos) {
1004     return Pos.reAssign(NewCont);
1005   };
1006   return processIteratorPositions(State, MatchContAndCompare, ReAssign);
1007 }
1008 
1009 // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
1010 // `OldSym - Int` to `NewSym - Int` and  `OldSym` to `NewSym` in any iterator
1011 // position offsets where `CondSym` is true.
rebaseSymbolInIteratorPositionsIf(ProgramStateRef State,SValBuilder & SVB,SymbolRef OldSym,SymbolRef NewSym,SymbolRef CondSym,BinaryOperator::Opcode Opc)1012 ProgramStateRef rebaseSymbolInIteratorPositionsIf(
1013     ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
1014     SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
1015   auto LessThanEnd = [&](const IteratorPosition &Pos) {
1016     return compare(State, Pos.getOffset(), CondSym, Opc);
1017   };
1018   auto RebaseSymbol = [&](const IteratorPosition &Pos) {
1019     return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
1020                                    NewSym));
1021   };
1022   return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
1023 }
1024 
1025 // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
1026 // `OldExpr - Int` to `NewExpr - Int` and  `OldExpr` to `NewExpr` in expression
1027 // `OrigExpr`.
rebaseSymbol(ProgramStateRef State,SValBuilder & SVB,SymbolRef OrigExpr,SymbolRef OldExpr,SymbolRef NewSym)1028 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
1029                        SymbolRef OrigExpr, SymbolRef OldExpr,
1030                        SymbolRef NewSym) {
1031   auto &SymMgr = SVB.getSymbolManager();
1032   auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
1033                               nonloc::SymbolVal(OldExpr),
1034                               SymMgr.getType(OrigExpr));
1035 
1036   const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
1037   if (!DiffInt)
1038     return OrigExpr;
1039 
1040   return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
1041                          SymMgr.getType(OrigExpr)).getAsSymbol();
1042 }
1043 
hasLiveIterators(ProgramStateRef State,const MemRegion * Cont)1044 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
1045   auto RegionMap = State->get<IteratorRegionMap>();
1046   for (const auto &Reg : RegionMap) {
1047     if (Reg.second.getContainer() == Cont)
1048       return true;
1049   }
1050 
1051   auto SymbolMap = State->get<IteratorSymbolMap>();
1052   for (const auto &Sym : SymbolMap) {
1053     if (Sym.second.getContainer() == Cont)
1054       return true;
1055   }
1056 
1057   return false;
1058 }
1059 
1060 } // namespace
1061 
registerContainerModeling(CheckerManager & mgr)1062 void ento::registerContainerModeling(CheckerManager &mgr) {
1063   mgr.registerChecker<ContainerModeling>();
1064 }
1065 
shouldRegisterContainerModeling(const CheckerManager & mgr)1066 bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) {
1067   if (!mgr.getLangOpts().CPlusPlus)
1068     return false;
1069 
1070   if (!mgr.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) {
1071     mgr.getASTContext().getDiagnostics().Report(
1072         diag::err_analyzer_checker_incompatible_analyzer_option)
1073       << "aggressive-binary-operation-simplification" << "false";
1074     return false;
1075   }
1076 
1077   return true;
1078 }
1079