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/CallEvent.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19 #include "llvm/ADT/Optional.h"
20 #include "llvm/ADT/SmallVector.h"
22 using namespace clang;
26 class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> {
28 // It sets the predefined invariant ('CDM') if the current call not break it.
29 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
31 // It reports whether a predefined invariant ('CDM') is broken.
32 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
35 // The pairs are in the following form: {{{class, call}}, return value}
36 const CallDescriptionMap<bool> CDM = {
37 // These are known in the LLVM project: 'Error()'
38 {{{"ARMAsmParser", "Error"}}, true},
39 {{{"HexagonAsmParser", "Error"}}, true},
40 {{{"LLLexer", "Error"}}, true},
41 {{{"LLParser", "Error"}}, true},
42 {{{"MCAsmParser", "Error"}}, true},
43 {{{"MCAsmParserExtension", "Error"}}, true},
44 {{{"TGParser", "Error"}}, true},
45 {{{"X86AsmParser", "Error"}}, true},
47 {{{"LLParser", "TokError"}}, true},
48 {{{"MCAsmParser", "TokError"}}, true},
49 {{{"MCAsmParserExtension", "TokError"}}, true},
50 {{{"TGParser", "TokError"}}, true},
52 {{{"MIParser", "error"}}, true},
53 {{{"WasmAsmParser", "error"}}, true},
54 {{{"WebAssemblyAsmParser", "error"}}, true},
56 {{{"AsmParser", "printError"}}, true}};
60 static std::string getName(const CallEvent &Call) {
61 std::string Name = "";
62 if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl()))
63 if (const CXXRecordDecl *RD = MD->getParent())
64 Name += RD->getNameAsString() + "::";
66 Name += Call.getCalleeIdentifier()->getName();
70 // The predefinitions ('CDM') could break due to the ever growing code base.
71 // Check for the expected invariants and see whether they apply.
72 static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
74 auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
79 return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
81 return C.getState()->isNull(*ReturnDV).isConstrainedFalse();
84 void ReturnValueChecker::checkPostCall(const CallEvent &Call,
85 CheckerContext &C) const {
86 const bool *RawExpectedValue = CDM.lookup(Call);
87 if (!RawExpectedValue)
90 SVal ReturnV = Call.getReturnValue();
91 bool ExpectedValue = *RawExpectedValue;
92 Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
93 if (!IsInvariantBreak)
96 // If the invariant is broken it is reported by 'checkEndFunction()'.
97 if (*IsInvariantBreak)
100 std::string Name = getName(Call);
101 const NoteTag *CallTag = C.getNoteTag(
102 [Name, ExpectedValue](BugReport &) -> std::string {
103 SmallString<128> Msg;
104 llvm::raw_svector_ostream Out(Msg);
106 Out << '\'' << Name << "' returns "
107 << (ExpectedValue ? "true" : "false");
110 /*IsPrunable=*/true);
112 ProgramStateRef State = C.getState();
113 State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue);
114 C.addTransition(State, CallTag);
117 void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
118 CheckerContext &C) const {
119 if (!RS || !RS->getRetValue())
122 // We cannot get the caller in the top-frame.
123 const StackFrameContext *SFC = C.getStackFrame();
124 if (C.getStackFrame()->inTopFrame())
127 ProgramStateRef State = C.getState();
128 CallEventManager &CMgr = C.getStateManager().getCallEventManager();
129 CallEventRef<> Call = CMgr.getCaller(SFC, State);
133 const bool *RawExpectedValue = CDM.lookup(*Call);
134 if (!RawExpectedValue)
137 SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
138 bool ExpectedValue = *RawExpectedValue;
139 Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
140 if (!IsInvariantBreak)
143 // If the invariant is appropriate it is reported by 'checkPostCall()'.
144 if (!*IsInvariantBreak)
147 std::string Name = getName(*Call);
148 const NoteTag *CallTag = C.getNoteTag(
149 [Name, ExpectedValue](BugReport &BR) -> std::string {
150 SmallString<128> Msg;
151 llvm::raw_svector_ostream Out(Msg);
153 // The following is swapped because the invariant is broken.
154 Out << '\'' << Name << "' returns "
155 << (ExpectedValue ? "false" : "true");
159 /*IsPrunable=*/false);
161 C.addTransition(State, CallTag);
164 void ento::registerReturnValueChecker(CheckerManager &Mgr) {
165 Mgr.registerChecker<ReturnValueChecker>();
168 bool ento::shouldRegisterReturnValueChecker(const LangOptions &LO) {