1 //===- ReturnValueChecker - Applies guaranteed return values ----*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This defines ReturnValueChecker, which checks for calls with guaranteed
10 // boolean return value. It ensures the return value of each function call.
12 //===----------------------------------------------------------------------===//
14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15 #include "clang/StaticAnalyzer/Core/Checker.h"
16 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20 #include "llvm/ADT/SmallVector.h"
23 using namespace clang
;
27 class ReturnValueChecker
: public Checker
<check::PostCall
, check::EndFunction
> {
29 // It sets the predefined invariant ('CDM') if the current call not break it.
30 void checkPostCall(const CallEvent
&Call
, CheckerContext
&C
) const;
32 // It reports whether a predefined invariant ('CDM') is broken.
33 void checkEndFunction(const ReturnStmt
*RS
, CheckerContext
&C
) const;
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},
48 {{{"LLParser", "TokError"}}, true},
49 {{{"MCAsmParser", "TokError"}}, true},
50 {{{"MCAsmParserExtension", "TokError"}}, true},
51 {{{"TGParser", "TokError"}}, true},
53 {{{"MIParser", "error"}}, true},
54 {{{"WasmAsmParser", "error"}}, true},
55 {{{"WebAssemblyAsmParser", "error"}}, true},
57 {{{"AsmParser", "printError"}}, true}};
61 static std::string
getName(const CallEvent
&Call
) {
63 if (const auto *MD
= dyn_cast
<CXXMethodDecl
>(Call
.getDecl()))
64 if (const CXXRecordDecl
*RD
= MD
->getParent())
65 Name
+= RD
->getNameAsString() + "::";
67 Name
+= Call
.getCalleeIdentifier()->getName();
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 std::optional
<bool> isInvariantBreak(bool ExpectedValue
, SVal ReturnV
,
75 auto ReturnDV
= ReturnV
.getAs
<DefinedOrUnknownSVal
>();
80 return C
.getState()->isNull(*ReturnDV
).isConstrainedTrue();
82 return C
.getState()->isNull(*ReturnDV
).isConstrainedFalse();
85 void ReturnValueChecker::checkPostCall(const CallEvent
&Call
,
86 CheckerContext
&C
) const {
87 const bool *RawExpectedValue
= CDM
.lookup(Call
);
88 if (!RawExpectedValue
)
91 SVal ReturnV
= Call
.getReturnValue();
92 bool ExpectedValue
= *RawExpectedValue
;
93 std::optional
<bool> IsInvariantBreak
=
94 isInvariantBreak(ExpectedValue
, ReturnV
, C
);
95 if (!IsInvariantBreak
)
98 // If the invariant is broken it is reported by 'checkEndFunction()'.
99 if (*IsInvariantBreak
)
102 std::string Name
= getName(Call
);
103 const NoteTag
*CallTag
= C
.getNoteTag(
104 [Name
, ExpectedValue
](PathSensitiveBugReport
&) -> std::string
{
105 SmallString
<128> Msg
;
106 llvm::raw_svector_ostream
Out(Msg
);
108 Out
<< '\'' << Name
<< "' returns "
109 << (ExpectedValue
? "true" : "false");
110 return std::string(Out
.str());
112 /*IsPrunable=*/true);
114 ProgramStateRef State
= C
.getState();
115 State
= State
->assume(ReturnV
.castAs
<DefinedOrUnknownSVal
>(), ExpectedValue
);
116 C
.addTransition(State
, CallTag
);
119 void ReturnValueChecker::checkEndFunction(const ReturnStmt
*RS
,
120 CheckerContext
&C
) const {
121 if (!RS
|| !RS
->getRetValue())
124 // We cannot get the caller in the top-frame.
125 const StackFrameContext
*SFC
= C
.getStackFrame();
126 if (C
.getStackFrame()->inTopFrame())
129 ProgramStateRef State
= C
.getState();
130 CallEventManager
&CMgr
= C
.getStateManager().getCallEventManager();
131 CallEventRef
<> Call
= CMgr
.getCaller(SFC
, State
);
135 const bool *RawExpectedValue
= CDM
.lookup(*Call
);
136 if (!RawExpectedValue
)
139 SVal ReturnV
= State
->getSVal(RS
->getRetValue(), C
.getLocationContext());
140 bool ExpectedValue
= *RawExpectedValue
;
141 std::optional
<bool> IsInvariantBreak
=
142 isInvariantBreak(ExpectedValue
, ReturnV
, C
);
143 if (!IsInvariantBreak
)
146 // If the invariant is appropriate it is reported by 'checkPostCall()'.
147 if (!*IsInvariantBreak
)
150 std::string Name
= getName(*Call
);
151 const NoteTag
*CallTag
= C
.getNoteTag(
152 [Name
, ExpectedValue
](BugReport
&BR
) -> std::string
{
153 SmallString
<128> Msg
;
154 llvm::raw_svector_ostream
Out(Msg
);
156 // The following is swapped because the invariant is broken.
157 Out
<< '\'' << Name
<< "' returns "
158 << (ExpectedValue
? "false" : "true");
160 return std::string(Out
.str());
162 /*IsPrunable=*/false);
164 C
.addTransition(State
, CallTag
);
167 void ento::registerReturnValueChecker(CheckerManager
&Mgr
) {
168 Mgr
.registerChecker
<ReturnValueChecker
>();
171 bool ento::shouldRegisterReturnValueChecker(const CheckerManager
&mgr
) {