106f32e7eSjoerg //== CheckerContext.h - Context info for path-sensitive checkers--*- C++ -*--=// 206f32e7eSjoerg // 306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information. 506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 606f32e7eSjoerg // 706f32e7eSjoerg //===----------------------------------------------------------------------===// 806f32e7eSjoerg // 906f32e7eSjoerg // This file defines CheckerContext that provides contextual info for 1006f32e7eSjoerg // path-sensitive checkers. 1106f32e7eSjoerg // 1206f32e7eSjoerg //===----------------------------------------------------------------------===// 1306f32e7eSjoerg 1406f32e7eSjoerg #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CHECKERCONTEXT_H 1506f32e7eSjoerg #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CHECKERCONTEXT_H 1606f32e7eSjoerg 1706f32e7eSjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 1806f32e7eSjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 1906f32e7eSjoerg 2006f32e7eSjoerg namespace clang { 2106f32e7eSjoerg namespace ento { 2206f32e7eSjoerg 2306f32e7eSjoerg class CheckerContext { 2406f32e7eSjoerg ExprEngine &Eng; 2506f32e7eSjoerg /// The current exploded(symbolic execution) graph node. 2606f32e7eSjoerg ExplodedNode *Pred; 2706f32e7eSjoerg /// The flag is true if the (state of the execution) has been modified 2806f32e7eSjoerg /// by the checker using this context. For example, a new transition has been 2906f32e7eSjoerg /// added or a bug report issued. 3006f32e7eSjoerg bool Changed; 3106f32e7eSjoerg /// The tagged location, which is used to generate all new nodes. 3206f32e7eSjoerg const ProgramPoint Location; 3306f32e7eSjoerg NodeBuilder &NB; 3406f32e7eSjoerg 3506f32e7eSjoerg public: 3606f32e7eSjoerg /// If we are post visiting a call, this flag will be set if the 3706f32e7eSjoerg /// call was inlined. In all other cases it will be false. 3806f32e7eSjoerg const bool wasInlined; 3906f32e7eSjoerg 4006f32e7eSjoerg CheckerContext(NodeBuilder &builder, 4106f32e7eSjoerg ExprEngine &eng, 4206f32e7eSjoerg ExplodedNode *pred, 4306f32e7eSjoerg const ProgramPoint &loc, 4406f32e7eSjoerg bool wasInlined = false) Eng(eng)4506f32e7eSjoerg : Eng(eng), 4606f32e7eSjoerg Pred(pred), 4706f32e7eSjoerg Changed(false), 4806f32e7eSjoerg Location(loc), 4906f32e7eSjoerg NB(builder), 5006f32e7eSjoerg wasInlined(wasInlined) { 5106f32e7eSjoerg assert(Pred->getState() && 5206f32e7eSjoerg "We should not call the checkers on an empty state."); 5306f32e7eSjoerg } 5406f32e7eSjoerg getAnalysisManager()5506f32e7eSjoerg AnalysisManager &getAnalysisManager() { 5606f32e7eSjoerg return Eng.getAnalysisManager(); 5706f32e7eSjoerg } 5806f32e7eSjoerg getConstraintManager()5906f32e7eSjoerg ConstraintManager &getConstraintManager() { 6006f32e7eSjoerg return Eng.getConstraintManager(); 6106f32e7eSjoerg } 6206f32e7eSjoerg getStoreManager()6306f32e7eSjoerg StoreManager &getStoreManager() { 6406f32e7eSjoerg return Eng.getStoreManager(); 6506f32e7eSjoerg } 6606f32e7eSjoerg 6706f32e7eSjoerg /// Returns the previous node in the exploded graph, which includes 6806f32e7eSjoerg /// the state of the program before the checker ran. Note, checkers should 6906f32e7eSjoerg /// not retain the node in their state since the nodes might get invalidated. getPredecessor()7006f32e7eSjoerg ExplodedNode *getPredecessor() { return Pred; } getState()7106f32e7eSjoerg const ProgramStateRef &getState() const { return Pred->getState(); } 7206f32e7eSjoerg 7306f32e7eSjoerg /// Check if the checker changed the state of the execution; ex: added 7406f32e7eSjoerg /// a new transition or a bug report. isDifferent()7506f32e7eSjoerg bool isDifferent() { return Changed; } 7606f32e7eSjoerg 7706f32e7eSjoerg /// Returns the number of times the current block has been visited 7806f32e7eSjoerg /// along the analyzed path. blockCount()7906f32e7eSjoerg unsigned blockCount() const { 8006f32e7eSjoerg return NB.getContext().blockCount(); 8106f32e7eSjoerg } 8206f32e7eSjoerg getASTContext()8306f32e7eSjoerg ASTContext &getASTContext() { 8406f32e7eSjoerg return Eng.getContext(); 8506f32e7eSjoerg } 8606f32e7eSjoerg getLangOpts()8706f32e7eSjoerg const LangOptions &getLangOpts() const { 8806f32e7eSjoerg return Eng.getContext().getLangOpts(); 8906f32e7eSjoerg } 9006f32e7eSjoerg getLocationContext()9106f32e7eSjoerg const LocationContext *getLocationContext() const { 9206f32e7eSjoerg return Pred->getLocationContext(); 9306f32e7eSjoerg } 9406f32e7eSjoerg getStackFrame()9506f32e7eSjoerg const StackFrameContext *getStackFrame() const { 9606f32e7eSjoerg return Pred->getStackFrame(); 9706f32e7eSjoerg } 9806f32e7eSjoerg 9906f32e7eSjoerg /// Return true if the current LocationContext has no caller context. inTopFrame()10006f32e7eSjoerg bool inTopFrame() const { return getLocationContext()->inTopFrame(); } 10106f32e7eSjoerg getBugReporter()10206f32e7eSjoerg BugReporter &getBugReporter() { 10306f32e7eSjoerg return Eng.getBugReporter(); 10406f32e7eSjoerg } 10506f32e7eSjoerg getSourceManager()10606f32e7eSjoerg const SourceManager &getSourceManager() { 10706f32e7eSjoerg return getBugReporter().getSourceManager(); 10806f32e7eSjoerg } 10906f32e7eSjoerg getPreprocessor()110*13fbcb42Sjoerg Preprocessor &getPreprocessor() { return getBugReporter().getPreprocessor(); } 111*13fbcb42Sjoerg getSValBuilder()11206f32e7eSjoerg SValBuilder &getSValBuilder() { 11306f32e7eSjoerg return Eng.getSValBuilder(); 11406f32e7eSjoerg } 11506f32e7eSjoerg getSymbolManager()11606f32e7eSjoerg SymbolManager &getSymbolManager() { 11706f32e7eSjoerg return getSValBuilder().getSymbolManager(); 11806f32e7eSjoerg } 11906f32e7eSjoerg getStateManager()12006f32e7eSjoerg ProgramStateManager &getStateManager() { 12106f32e7eSjoerg return Eng.getStateManager(); 12206f32e7eSjoerg } 12306f32e7eSjoerg getCurrentAnalysisDeclContext()12406f32e7eSjoerg AnalysisDeclContext *getCurrentAnalysisDeclContext() const { 12506f32e7eSjoerg return Pred->getLocationContext()->getAnalysisDeclContext(); 12606f32e7eSjoerg } 12706f32e7eSjoerg 12806f32e7eSjoerg /// Get the blockID. getBlockID()12906f32e7eSjoerg unsigned getBlockID() const { 13006f32e7eSjoerg return NB.getContext().getBlock()->getBlockID(); 13106f32e7eSjoerg } 13206f32e7eSjoerg 13306f32e7eSjoerg /// If the given node corresponds to a PostStore program point, 13406f32e7eSjoerg /// retrieve the location region as it was uttered in the code. 13506f32e7eSjoerg /// 13606f32e7eSjoerg /// This utility can be useful for generating extensive diagnostics, for 13706f32e7eSjoerg /// example, for finding variables that the given symbol was assigned to. getLocationRegionIfPostStore(const ExplodedNode * N)13806f32e7eSjoerg static const MemRegion *getLocationRegionIfPostStore(const ExplodedNode *N) { 13906f32e7eSjoerg ProgramPoint L = N->getLocation(); 14006f32e7eSjoerg if (Optional<PostStore> PSL = L.getAs<PostStore>()) 14106f32e7eSjoerg return reinterpret_cast<const MemRegion*>(PSL->getLocationValue()); 14206f32e7eSjoerg return nullptr; 14306f32e7eSjoerg } 14406f32e7eSjoerg 14506f32e7eSjoerg /// Get the value of arbitrary expressions at this point in the path. getSVal(const Stmt * S)14606f32e7eSjoerg SVal getSVal(const Stmt *S) const { 14706f32e7eSjoerg return Pred->getSVal(S); 14806f32e7eSjoerg } 14906f32e7eSjoerg 15006f32e7eSjoerg /// Returns true if the value of \p E is greater than or equal to \p 15106f32e7eSjoerg /// Val under unsigned comparison 15206f32e7eSjoerg bool isGreaterOrEqual(const Expr *E, unsigned long long Val); 15306f32e7eSjoerg 15406f32e7eSjoerg /// Returns true if the value of \p E is negative. 15506f32e7eSjoerg bool isNegative(const Expr *E); 15606f32e7eSjoerg 15706f32e7eSjoerg /// Generates a new transition in the program state graph 15806f32e7eSjoerg /// (ExplodedGraph). Uses the default CheckerContext predecessor node. 15906f32e7eSjoerg /// 16006f32e7eSjoerg /// @param State The state of the generated node. If not specified, the state 16106f32e7eSjoerg /// will not be changed, but the new node will have the checker's tag. 16206f32e7eSjoerg /// @param Tag The tag is used to uniquely identify the creation site. If no 16306f32e7eSjoerg /// tag is specified, a default tag, unique to the given checker, 16406f32e7eSjoerg /// will be used. Tags are used to prevent states generated at 16506f32e7eSjoerg /// different sites from caching out. 16606f32e7eSjoerg ExplodedNode *addTransition(ProgramStateRef State = nullptr, 16706f32e7eSjoerg const ProgramPointTag *Tag = nullptr) { 16806f32e7eSjoerg return addTransitionImpl(State ? State : getState(), false, nullptr, Tag); 16906f32e7eSjoerg } 17006f32e7eSjoerg 17106f32e7eSjoerg /// Generates a new transition with the given predecessor. 17206f32e7eSjoerg /// Allows checkers to generate a chain of nodes. 17306f32e7eSjoerg /// 17406f32e7eSjoerg /// @param State The state of the generated node. 17506f32e7eSjoerg /// @param Pred The transition will be generated from the specified Pred node 17606f32e7eSjoerg /// to the newly generated node. 17706f32e7eSjoerg /// @param Tag The tag to uniquely identify the creation site. 178*13fbcb42Sjoerg ExplodedNode *addTransition(ProgramStateRef State, ExplodedNode *Pred, 17906f32e7eSjoerg const ProgramPointTag *Tag = nullptr) { 18006f32e7eSjoerg return addTransitionImpl(State, false, Pred, Tag); 18106f32e7eSjoerg } 18206f32e7eSjoerg 18306f32e7eSjoerg /// Generate a sink node. Generating a sink stops exploration of the 18406f32e7eSjoerg /// given path. To create a sink node for the purpose of reporting an error, 18506f32e7eSjoerg /// checkers should use generateErrorNode() instead. 18606f32e7eSjoerg ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred, 18706f32e7eSjoerg const ProgramPointTag *Tag = nullptr) { 18806f32e7eSjoerg return addTransitionImpl(State ? State : getState(), true, Pred, Tag); 18906f32e7eSjoerg } 19006f32e7eSjoerg 191*13fbcb42Sjoerg /// Add a sink node to the current path of execution, halting analysis. 192*13fbcb42Sjoerg void addSink(ProgramStateRef State = nullptr, 193*13fbcb42Sjoerg const ProgramPointTag *Tag = nullptr) { 194*13fbcb42Sjoerg if (!State) 195*13fbcb42Sjoerg State = getState(); 196*13fbcb42Sjoerg addTransition(State, generateSink(State, getPredecessor())); 197*13fbcb42Sjoerg } 198*13fbcb42Sjoerg 19906f32e7eSjoerg /// Generate a transition to a node that will be used to report 20006f32e7eSjoerg /// an error. This node will be a sink. That is, it will stop exploration of 20106f32e7eSjoerg /// the given path. 20206f32e7eSjoerg /// 20306f32e7eSjoerg /// @param State The state of the generated node. 20406f32e7eSjoerg /// @param Tag The tag to uniquely identify the creation site. If null, 20506f32e7eSjoerg /// the default tag for the checker will be used. 20606f32e7eSjoerg ExplodedNode *generateErrorNode(ProgramStateRef State = nullptr, 20706f32e7eSjoerg const ProgramPointTag *Tag = nullptr) { 20806f32e7eSjoerg return generateSink(State, Pred, 20906f32e7eSjoerg (Tag ? Tag : Location.getTag())); 21006f32e7eSjoerg } 21106f32e7eSjoerg 21206f32e7eSjoerg /// Generate a transition to a node that will be used to report 21306f32e7eSjoerg /// an error. This node will not be a sink. That is, exploration will 21406f32e7eSjoerg /// continue along this path. 21506f32e7eSjoerg /// 21606f32e7eSjoerg /// @param State The state of the generated node. 21706f32e7eSjoerg /// @param Tag The tag to uniquely identify the creation site. If null, 21806f32e7eSjoerg /// the default tag for the checker will be used. 21906f32e7eSjoerg ExplodedNode * 22006f32e7eSjoerg generateNonFatalErrorNode(ProgramStateRef State = nullptr, 22106f32e7eSjoerg const ProgramPointTag *Tag = nullptr) { 22206f32e7eSjoerg return addTransition(State, (Tag ? Tag : Location.getTag())); 22306f32e7eSjoerg } 22406f32e7eSjoerg 225*13fbcb42Sjoerg /// Generate a transition to a node that will be used to report 226*13fbcb42Sjoerg /// an error. This node will not be a sink. That is, exploration will 227*13fbcb42Sjoerg /// continue along this path. 228*13fbcb42Sjoerg /// 229*13fbcb42Sjoerg /// @param State The state of the generated node. 230*13fbcb42Sjoerg /// @param Pred The transition will be generated from the specified Pred node 231*13fbcb42Sjoerg /// to the newly generated node. 232*13fbcb42Sjoerg /// @param Tag The tag to uniquely identify the creation site. If null, 233*13fbcb42Sjoerg /// the default tag for the checker will be used. 234*13fbcb42Sjoerg ExplodedNode * 235*13fbcb42Sjoerg generateNonFatalErrorNode(ProgramStateRef State, 236*13fbcb42Sjoerg ExplodedNode *Pred, 237*13fbcb42Sjoerg const ProgramPointTag *Tag = nullptr) { 238*13fbcb42Sjoerg return addTransition(State, Pred, (Tag ? Tag : Location.getTag())); 239*13fbcb42Sjoerg } 240*13fbcb42Sjoerg 24106f32e7eSjoerg /// Emit the diagnostics report. emitReport(std::unique_ptr<BugReport> R)24206f32e7eSjoerg void emitReport(std::unique_ptr<BugReport> R) { 24306f32e7eSjoerg Changed = true; 24406f32e7eSjoerg Eng.getBugReporter().emitReport(std::move(R)); 24506f32e7eSjoerg } 24606f32e7eSjoerg 24706f32e7eSjoerg /// Produce a program point tag that displays an additional path note 24806f32e7eSjoerg /// to the user. This is a lightweight alternative to the 24906f32e7eSjoerg /// BugReporterVisitor mechanism: instead of visiting the bug report 25006f32e7eSjoerg /// node-by-node to restore the sequence of events that led to discovering 25106f32e7eSjoerg /// a bug, you can add notes as you add your transitions. 25206f32e7eSjoerg /// 25306f32e7eSjoerg /// @param Cb Callback with 'BugReporterContext &, BugReport &' parameters. 25406f32e7eSjoerg /// @param IsPrunable Whether the note is prunable. It allows BugReporter 25506f32e7eSjoerg /// to omit the note from the report if it would make the displayed 25606f32e7eSjoerg /// bug path significantly shorter. 25706f32e7eSjoerg const NoteTag *getNoteTag(NoteTag::Callback &&Cb, bool IsPrunable = false) { 25806f32e7eSjoerg return Eng.getNoteTags().makeNoteTag(std::move(Cb), IsPrunable); 25906f32e7eSjoerg } 26006f32e7eSjoerg 26106f32e7eSjoerg /// A shorthand version of getNoteTag that doesn't require you to accept 26206f32e7eSjoerg /// the 'BugReporterContext' argument when you don't need it. 26306f32e7eSjoerg /// 26406f32e7eSjoerg /// @param Cb Callback only with 'BugReport &' parameter. 26506f32e7eSjoerg /// @param IsPrunable Whether the note is prunable. It allows BugReporter 26606f32e7eSjoerg /// to omit the note from the report if it would make the displayed 26706f32e7eSjoerg /// bug path significantly shorter. 268*13fbcb42Sjoerg const NoteTag 269*13fbcb42Sjoerg *getNoteTag(std::function<std::string(PathSensitiveBugReport &)> &&Cb, 27006f32e7eSjoerg bool IsPrunable = false) { 27106f32e7eSjoerg return getNoteTag( 272*13fbcb42Sjoerg [Cb](BugReporterContext &, 273*13fbcb42Sjoerg PathSensitiveBugReport &BR) { return Cb(BR); }, 27406f32e7eSjoerg IsPrunable); 27506f32e7eSjoerg } 27606f32e7eSjoerg 27706f32e7eSjoerg /// A shorthand version of getNoteTag that doesn't require you to accept 27806f32e7eSjoerg /// the arguments when you don't need it. 27906f32e7eSjoerg /// 28006f32e7eSjoerg /// @param Cb Callback without parameters. 28106f32e7eSjoerg /// @param IsPrunable Whether the note is prunable. It allows BugReporter 28206f32e7eSjoerg /// to omit the note from the report if it would make the displayed 28306f32e7eSjoerg /// bug path significantly shorter. 28406f32e7eSjoerg const NoteTag *getNoteTag(std::function<std::string()> &&Cb, 28506f32e7eSjoerg bool IsPrunable = false) { 286*13fbcb42Sjoerg return getNoteTag([Cb](BugReporterContext &, 287*13fbcb42Sjoerg PathSensitiveBugReport &) { return Cb(); }, 28806f32e7eSjoerg IsPrunable); 28906f32e7eSjoerg } 29006f32e7eSjoerg 29106f32e7eSjoerg /// A shorthand version of getNoteTag that accepts a plain note. 29206f32e7eSjoerg /// 29306f32e7eSjoerg /// @param Note The note. 29406f32e7eSjoerg /// @param IsPrunable Whether the note is prunable. It allows BugReporter 29506f32e7eSjoerg /// to omit the note from the report if it would make the displayed 29606f32e7eSjoerg /// bug path significantly shorter. 29706f32e7eSjoerg const NoteTag *getNoteTag(StringRef Note, bool IsPrunable = false) { 29806f32e7eSjoerg return getNoteTag( 299*13fbcb42Sjoerg [Note](BugReporterContext &, 300*13fbcb42Sjoerg PathSensitiveBugReport &) { return std::string(Note); }, 301*13fbcb42Sjoerg IsPrunable); 302*13fbcb42Sjoerg } 303*13fbcb42Sjoerg 304*13fbcb42Sjoerg /// A shorthand version of getNoteTag that accepts a lambda with stream for 305*13fbcb42Sjoerg /// note. 306*13fbcb42Sjoerg /// 307*13fbcb42Sjoerg /// @param Cb Callback with 'BugReport &' and 'llvm::raw_ostream &'. 308*13fbcb42Sjoerg /// @param IsPrunable Whether the note is prunable. It allows BugReporter 309*13fbcb42Sjoerg /// to omit the note from the report if it would make the displayed 310*13fbcb42Sjoerg /// bug path significantly shorter. 311*13fbcb42Sjoerg const NoteTag *getNoteTag( 312*13fbcb42Sjoerg std::function<void(PathSensitiveBugReport &BR, llvm::raw_ostream &OS)> &&Cb, 313*13fbcb42Sjoerg bool IsPrunable = false) { 314*13fbcb42Sjoerg return getNoteTag( 315*13fbcb42Sjoerg [Cb](PathSensitiveBugReport &BR) -> std::string { 316*13fbcb42Sjoerg llvm::SmallString<128> Str; 317*13fbcb42Sjoerg llvm::raw_svector_ostream OS(Str); 318*13fbcb42Sjoerg Cb(BR, OS); 319*13fbcb42Sjoerg return std::string(OS.str()); 320*13fbcb42Sjoerg }, 321*13fbcb42Sjoerg IsPrunable); 32206f32e7eSjoerg } 32306f32e7eSjoerg 32406f32e7eSjoerg /// Returns the word that should be used to refer to the declaration 32506f32e7eSjoerg /// in the report. 32606f32e7eSjoerg StringRef getDeclDescription(const Decl *D); 32706f32e7eSjoerg 32806f32e7eSjoerg /// Get the declaration of the called function (path-sensitive). 32906f32e7eSjoerg const FunctionDecl *getCalleeDecl(const CallExpr *CE) const; 33006f32e7eSjoerg 33106f32e7eSjoerg /// Get the name of the called function (path-sensitive). 33206f32e7eSjoerg StringRef getCalleeName(const FunctionDecl *FunDecl) const; 33306f32e7eSjoerg 33406f32e7eSjoerg /// Get the identifier of the called function (path-sensitive). getCalleeIdentifier(const CallExpr * CE)33506f32e7eSjoerg const IdentifierInfo *getCalleeIdentifier(const CallExpr *CE) const { 33606f32e7eSjoerg const FunctionDecl *FunDecl = getCalleeDecl(CE); 33706f32e7eSjoerg if (FunDecl) 33806f32e7eSjoerg return FunDecl->getIdentifier(); 33906f32e7eSjoerg else 34006f32e7eSjoerg return nullptr; 34106f32e7eSjoerg } 34206f32e7eSjoerg 34306f32e7eSjoerg /// Get the name of the called function (path-sensitive). getCalleeName(const CallExpr * CE)34406f32e7eSjoerg StringRef getCalleeName(const CallExpr *CE) const { 34506f32e7eSjoerg const FunctionDecl *FunDecl = getCalleeDecl(CE); 34606f32e7eSjoerg return getCalleeName(FunDecl); 34706f32e7eSjoerg } 34806f32e7eSjoerg 34906f32e7eSjoerg /// Returns true if the callee is an externally-visible function in the 35006f32e7eSjoerg /// top-level namespace, such as \c malloc. 35106f32e7eSjoerg /// 35206f32e7eSjoerg /// If a name is provided, the function must additionally match the given 35306f32e7eSjoerg /// name. 35406f32e7eSjoerg /// 35506f32e7eSjoerg /// Note that this deliberately excludes C++ library functions in the \c std 35606f32e7eSjoerg /// namespace, but will include C library functions accessed through the 35706f32e7eSjoerg /// \c std namespace. This also does not check if the function is declared 35806f32e7eSjoerg /// as 'extern "C"', or if it uses C++ name mangling. 35906f32e7eSjoerg static bool isCLibraryFunction(const FunctionDecl *FD, 36006f32e7eSjoerg StringRef Name = StringRef()); 36106f32e7eSjoerg 36206f32e7eSjoerg /// Depending on wither the location corresponds to a macro, return 36306f32e7eSjoerg /// either the macro name or the token spelling. 36406f32e7eSjoerg /// 36506f32e7eSjoerg /// This could be useful when checkers' logic depends on whether a function 36606f32e7eSjoerg /// is called with a given macro argument. For example: 36706f32e7eSjoerg /// s = socket(AF_INET,..) 36806f32e7eSjoerg /// If AF_INET is a macro, the result should be treated as a source of taint. 36906f32e7eSjoerg /// 37006f32e7eSjoerg /// \sa clang::Lexer::getSpelling(), clang::Lexer::getImmediateMacroName(). 37106f32e7eSjoerg StringRef getMacroNameOrSpelling(SourceLocation &Loc); 37206f32e7eSjoerg 37306f32e7eSjoerg private: 37406f32e7eSjoerg ExplodedNode *addTransitionImpl(ProgramStateRef State, 37506f32e7eSjoerg bool MarkAsSink, 37606f32e7eSjoerg ExplodedNode *P = nullptr, 37706f32e7eSjoerg const ProgramPointTag *Tag = nullptr) { 37806f32e7eSjoerg // The analyzer may stop exploring if it sees a state it has previously 37906f32e7eSjoerg // visited ("cache out"). The early return here is a defensive check to 38006f32e7eSjoerg // prevent accidental caching out by checker API clients. Unless there is a 38106f32e7eSjoerg // tag or the client checker has requested that the generated node be 38206f32e7eSjoerg // marked as a sink, we assume that a client requesting a transition to a 38306f32e7eSjoerg // state that is the same as the predecessor state has made a mistake. We 38406f32e7eSjoerg // return the predecessor rather than cache out. 38506f32e7eSjoerg // 38606f32e7eSjoerg // TODO: We could potentially change the return to an assertion to alert 38706f32e7eSjoerg // clients to their mistake, but several checkers (including 38806f32e7eSjoerg // DereferenceChecker, CallAndMessageChecker, and DynamicTypePropagation) 38906f32e7eSjoerg // rely upon the defensive behavior and would need to be updated. 39006f32e7eSjoerg if (!State || (State == Pred->getState() && !Tag && !MarkAsSink)) 39106f32e7eSjoerg return Pred; 39206f32e7eSjoerg 39306f32e7eSjoerg Changed = true; 39406f32e7eSjoerg const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); 39506f32e7eSjoerg if (!P) 39606f32e7eSjoerg P = Pred; 39706f32e7eSjoerg 39806f32e7eSjoerg ExplodedNode *node; 39906f32e7eSjoerg if (MarkAsSink) 40006f32e7eSjoerg node = NB.generateSink(LocalLoc, State, P); 40106f32e7eSjoerg else 40206f32e7eSjoerg node = NB.generateNode(LocalLoc, State, P); 40306f32e7eSjoerg return node; 40406f32e7eSjoerg } 40506f32e7eSjoerg }; 40606f32e7eSjoerg 40706f32e7eSjoerg } // end GR namespace 40806f32e7eSjoerg 40906f32e7eSjoerg } // end clang namespace 41006f32e7eSjoerg 41106f32e7eSjoerg #endif 412