clang  15.0.0git
ReturnValueChecker.cpp
Go to the documentation of this file.
1 //===- ReturnValueChecker - Applies guaranteed return values ----*- 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 // This defines ReturnValueChecker, which checks for calls with guaranteed
10 // boolean return value. It ensures the return value of each function call.
11 //
12 //===----------------------------------------------------------------------===//
13 
20 #include "llvm/ADT/Optional.h"
21 #include "llvm/ADT/SmallVector.h"
22 
23 using namespace clang;
24 using namespace ento;
25 
26 namespace {
27 class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> {
28 public:
29  // It sets the predefined invariant ('CDM') if the current call not break it.
30  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
31 
32  // It reports whether a predefined invariant ('CDM') is broken.
33  void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
34 
35 private:
36  // The pairs are in the following form: {{{class, call}}, return value}
37  const CallDescriptionMap<bool> CDM = {
38  // These are known in the LLVM project: 'Error()'
39  {{{"ARMAsmParser", "Error"}}, true},
40  {{{"HexagonAsmParser", "Error"}}, true},
41  {{{"LLLexer", "Error"}}, true},
42  {{{"LLParser", "Error"}}, true},
43  {{{"MCAsmParser", "Error"}}, true},
44  {{{"MCAsmParserExtension", "Error"}}, true},
45  {{{"TGParser", "Error"}}, true},
46  {{{"X86AsmParser", "Error"}}, true},
47  // 'TokError()'
48  {{{"LLParser", "TokError"}}, true},
49  {{{"MCAsmParser", "TokError"}}, true},
50  {{{"MCAsmParserExtension", "TokError"}}, true},
51  {{{"TGParser", "TokError"}}, true},
52  // 'error()'
53  {{{"MIParser", "error"}}, true},
54  {{{"WasmAsmParser", "error"}}, true},
55  {{{"WebAssemblyAsmParser", "error"}}, true},
56  // Other
57  {{{"AsmParser", "printError"}}, true}};
58 };
59 } // namespace
60 
61 static std::string getName(const CallEvent &Call) {
62  std::string Name;
63  if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl()))
64  if (const CXXRecordDecl *RD = MD->getParent())
65  Name += RD->getNameAsString() + "::";
66 
67  Name += Call.getCalleeIdentifier()->getName();
68  return Name;
69 }
70 
71 // The predefinitions ('CDM') could break due to the ever growing code base.
72 // Check for the expected invariants and see whether they apply.
73 static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
74  CheckerContext &C) {
75  auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
76  if (!ReturnDV)
77  return None;
78 
79  if (ExpectedValue)
80  return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
81 
82  return C.getState()->isNull(*ReturnDV).isConstrainedFalse();
83 }
84 
85 void ReturnValueChecker::checkPostCall(const CallEvent &Call,
86  CheckerContext &C) const {
87  const bool *RawExpectedValue = CDM.lookup(Call);
88  if (!RawExpectedValue)
89  return;
90 
91  SVal ReturnV = Call.getReturnValue();
92  bool ExpectedValue = *RawExpectedValue;
93  Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
94  if (!IsInvariantBreak)
95  return;
96 
97  // If the invariant is broken it is reported by 'checkEndFunction()'.
98  if (*IsInvariantBreak)
99  return;
100 
101  std::string Name = getName(Call);
102  const NoteTag *CallTag = C.getNoteTag(
103  [Name, ExpectedValue](PathSensitiveBugReport &) -> std::string {
104  SmallString<128> Msg;
105  llvm::raw_svector_ostream Out(Msg);
106 
107  Out << '\'' << Name << "' returns "
108  << (ExpectedValue ? "true" : "false");
109  return std::string(Out.str());
110  },
111  /*IsPrunable=*/true);
112 
113  ProgramStateRef State = C.getState();
114  State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue);
115  C.addTransition(State, CallTag);
116 }
117 
118 void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
119  CheckerContext &C) const {
120  if (!RS || !RS->getRetValue())
121  return;
122 
123  // We cannot get the caller in the top-frame.
124  const StackFrameContext *SFC = C.getStackFrame();
125  if (C.getStackFrame()->inTopFrame())
126  return;
127 
128  ProgramStateRef State = C.getState();
129  CallEventManager &CMgr = C.getStateManager().getCallEventManager();
130  CallEventRef<> Call = CMgr.getCaller(SFC, State);
131  if (!Call)
132  return;
133 
134  const bool *RawExpectedValue = CDM.lookup(*Call);
135  if (!RawExpectedValue)
136  return;
137 
138  SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
139  bool ExpectedValue = *RawExpectedValue;
140  Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
141  if (!IsInvariantBreak)
142  return;
143 
144  // If the invariant is appropriate it is reported by 'checkPostCall()'.
145  if (!*IsInvariantBreak)
146  return;
147 
148  std::string Name = getName(*Call);
149  const NoteTag *CallTag = C.getNoteTag(
150  [Name, ExpectedValue](BugReport &BR) -> std::string {
151  SmallString<128> Msg;
152  llvm::raw_svector_ostream Out(Msg);
153 
154  // The following is swapped because the invariant is broken.
155  Out << '\'' << Name << "' returns "
156  << (ExpectedValue ? "false" : "true");
157 
158  return std::string(Out.str());
159  },
160  /*IsPrunable=*/false);
161 
162  C.addTransition(State, CallTag);
163 }
164 
165 void ento::registerReturnValueChecker(CheckerManager &Mgr) {
166  Mgr.registerChecker<ReturnValueChecker>();
167 }
168 
169 bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) {
170  return true;
171 }
clang::ReturnStmt::getRetValue
Expr * getRetValue()
Definition: Stmt.h:2797
CallDescription.h
string
string(SUBSTRING ${CMAKE_CURRENT_BINARY_DIR} 0 ${PATH_LIB_START} PATH_HEAD) string(SUBSTRING $
Definition: CMakeLists.txt:22
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:55
isInvariantBreak
static Optional< bool > isInvariantBreak(bool ExpectedValue, SVal ReturnV, CheckerContext &C)
Definition: ReturnValueChecker.cpp:73
llvm::Optional< bool >
clang::StackFrameContext
It represents a stack frame of the call stack (based on CallEvent).
Definition: AnalysisDeclContext.h:295
clang::index::SymbolRole::Call
@ Call
CallEvent.h
BuiltinCheckerRegistration.h
CheckerManager.h
llvm::SmallString< 128 >
getName
static std::string getName(const CallEvent &Call)
Definition: ReturnValueChecker.cpp:61
clang::CXXRecordDecl
Represents a C++ struct/union/class.
Definition: DeclCXX.h:254
clang::DeclContext::getParent
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition: DeclBase.h:1876
State
LineState State
Definition: UnwrappedLineFormatter.cpp:1090
CheckerContext.h
Checker.h
clang
Definition: CalledOnceCheck.h:17
clang::ReturnStmt
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Definition: Stmt.h:2764