]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
Move all sources from the llvm project into contrib/llvm-project.
[FreeBSD/FreeBSD.git] / contrib / llvm-project / clang / lib / StaticAnalyzer / Checkers / ReturnValueChecker.cpp
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
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"
21
22 using namespace clang;
23 using namespace ento;
24
25 namespace {
26 class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> {
27 public:
28   // It sets the predefined invariant ('CDM') if the current call not break it.
29   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
30
31   // It reports whether a predefined invariant ('CDM') is broken.
32   void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
33
34 private:
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},
46       // 'TokError()'
47       {{{"LLParser", "TokError"}}, true},
48       {{{"MCAsmParser", "TokError"}}, true},
49       {{{"MCAsmParserExtension", "TokError"}}, true},
50       {{{"TGParser", "TokError"}}, true},
51       // 'error()'
52       {{{"MIParser", "error"}}, true},
53       {{{"WasmAsmParser", "error"}}, true},
54       {{{"WebAssemblyAsmParser", "error"}}, true},
55       // Other
56       {{{"AsmParser", "printError"}}, true}};
57 };
58 } // namespace
59
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() + "::";
65
66   Name += Call.getCalleeIdentifier()->getName();
67   return Name;
68 }
69
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,
73                                        CheckerContext &C) {
74   auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
75   if (!ReturnDV)
76     return None;
77
78   if (ExpectedValue)
79     return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
80
81   return C.getState()->isNull(*ReturnDV).isConstrainedFalse();
82 }
83
84 void ReturnValueChecker::checkPostCall(const CallEvent &Call,
85                                        CheckerContext &C) const {
86   const bool *RawExpectedValue = CDM.lookup(Call);
87   if (!RawExpectedValue)
88     return;
89
90   SVal ReturnV = Call.getReturnValue();
91   bool ExpectedValue = *RawExpectedValue;
92   Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
93   if (!IsInvariantBreak)
94     return;
95
96   // If the invariant is broken it is reported by 'checkEndFunction()'.
97   if (*IsInvariantBreak)
98     return;
99
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);
105
106         Out << '\'' << Name << "' returns "
107             << (ExpectedValue ? "true" : "false");
108         return Out.str();
109       },
110       /*IsPrunable=*/true);
111
112   ProgramStateRef State = C.getState();
113   State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue);
114   C.addTransition(State, CallTag);
115 }
116
117 void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
118                                           CheckerContext &C) const {
119   if (!RS || !RS->getRetValue())
120     return;
121
122   // We cannot get the caller in the top-frame.
123   const StackFrameContext *SFC = C.getStackFrame();
124   if (C.getStackFrame()->inTopFrame())
125     return;
126
127   ProgramStateRef State = C.getState();
128   CallEventManager &CMgr = C.getStateManager().getCallEventManager();
129   CallEventRef<> Call = CMgr.getCaller(SFC, State);
130   if (!Call)
131     return;
132
133   const bool *RawExpectedValue = CDM.lookup(*Call);
134   if (!RawExpectedValue)
135     return;
136
137   SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
138   bool ExpectedValue = *RawExpectedValue;
139   Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
140   if (!IsInvariantBreak)
141     return;
142
143   // If the invariant is appropriate it is reported by 'checkPostCall()'.
144   if (!*IsInvariantBreak)
145     return;
146
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);
152
153         // The following is swapped because the invariant is broken.
154         Out << '\'' << Name << "' returns "
155             << (ExpectedValue ? "false" : "true");
156
157         return Out.str();
158       },
159       /*IsPrunable=*/false);
160
161   C.addTransition(State, CallTag);
162 }
163
164 void ento::registerReturnValueChecker(CheckerManager &Mgr) {
165   Mgr.registerChecker<ReturnValueChecker>();
166 }
167
168 bool ento::shouldRegisterReturnValueChecker(const LangOptions &LO) {
169   return true;
170 }