Coverage Report

Created: 2023-09-12 09:32

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
Line
Count
Source (jump to first uncovered line)
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/CallDescription.h"
18
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20
#include "llvm/ADT/SmallVector.h"
21
#include <optional>
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
4
static std::string getName(const CallEvent &Call) {
62
4
  std::string Name;
63
4
  if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl()))
64
4
    if (const CXXRecordDecl *RD = MD->getParent())
65
4
      Name += RD->getNameAsString() + "::";
66
67
4
  Name += Call.getCalleeIdentifier()->getName();
68
4
  return Name;
69
4
}
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 std::optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
74
6
                                            CheckerContext &C) {
75
6
  auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
76
6
  if (!ReturnDV)
77
0
    return std::nullopt;
78
79
6
  if (ExpectedValue)
80
6
    return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
81
82
0
  return C.getState()->isNull(*ReturnDV).isConstrainedFalse();
83
6
}
84
85
void ReturnValueChecker::checkPostCall(const CallEvent &Call,
86
219
                                       CheckerContext &C) const {
87
219
  const bool *RawExpectedValue = CDM.lookup(Call);
88
219
  if (!RawExpectedValue)
89
215
    return;
90
91
4
  SVal ReturnV = Call.getReturnValue();
92
4
  bool ExpectedValue = *RawExpectedValue;
93
4
  std::optional<bool> IsInvariantBreak =
94
4
      isInvariantBreak(ExpectedValue, ReturnV, C);
95
4
  if (!IsInvariantBreak)
96
0
    return;
97
98
  // If the invariant is broken it is reported by 'checkEndFunction()'.
99
4
  if (*IsInvariantBreak)
100
2
    return;
101
102
2
  std::string Name = getName(Call);
103
2
  const NoteTag *CallTag = C.getNoteTag(
104
2
      [Name, ExpectedValue](PathSensitiveBugReport &) -> std::string {
105
1
        SmallString<128> Msg;
106
1
        llvm::raw_svector_ostream Out(Msg);
107
108
1
        Out << '\'' << Name << "' returns "
109
1
            << (ExpectedValue ? "true" : 
"false"0
);
110
1
        return std::string(Out.str());
111
1
      },
112
2
      /*IsPrunable=*/true);
113
114
2
  ProgramStateRef State = C.getState();
115
2
  State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue);
116
2
  C.addTransition(State, CallTag);
117
2
}
118
119
void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
120
125
                                          CheckerContext &C) const {
121
125
  if (!RS || 
!RS->getRetValue()34
)
122
91
    return;
123
124
  // We cannot get the caller in the top-frame.
125
34
  const StackFrameContext *SFC = C.getStackFrame();
126
34
  if (C.getStackFrame()->inTopFrame())
127
25
    return;
128
129
9
  ProgramStateRef State = C.getState();
130
9
  CallEventManager &CMgr = C.getStateManager().getCallEventManager();
131
9
  CallEventRef<> Call = CMgr.getCaller(SFC, State);
132
9
  if (!Call)
133
0
    return;
134
135
9
  const bool *RawExpectedValue = CDM.lookup(*Call);
136
9
  if (!RawExpectedValue)
137
7
    return;
138
139
2
  SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
140
2
  bool ExpectedValue = *RawExpectedValue;
141
2
  std::optional<bool> IsInvariantBreak =
142
2
      isInvariantBreak(ExpectedValue, ReturnV, C);
143
2
  if (!IsInvariantBreak)
144
0
    return;
145
146
  // If the invariant is appropriate it is reported by 'checkPostCall()'.
147
2
  if (!*IsInvariantBreak)
148
0
    return;
149
150
2
  std::string Name = getName(*Call);
151
2
  const NoteTag *CallTag = C.getNoteTag(
152
2
      [Name, ExpectedValue](BugReport &BR) -> std::string {
153
1
        SmallString<128> Msg;
154
1
        llvm::raw_svector_ostream Out(Msg);
155
156
        // The following is swapped because the invariant is broken.
157
1
        Out << '\'' << Name << "' returns "
158
1
            << (ExpectedValue ? "false" : 
"true"0
);
159
160
1
        return std::string(Out.str());
161
1
      },
162
2
      /*IsPrunable=*/false);
163
164
2
  C.addTransition(State, CallTag);
165
2
}
166
167
42
void ento::registerReturnValueChecker(CheckerManager &Mgr) {
168
42
  Mgr.registerChecker<ReturnValueChecker>();
169
42
}
170
171
88
bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) {
172
88
  return true;
173
88
}