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