xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===- ReturnValueChecker - Check methods always returning true -*- 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 models a very specific coding
10 // convention within the LLVM/Clang codebase: there several classes that have
11 // Error() methods which always return true.
12 // This checker was introduced to eliminate false positives caused by this
13 // peculiar "always returns true" invariant. (Normally, the analyzer assumes
14 // that a function returning `bool` can return both `true` and `false`, because
15 // otherwise it could've been a `void` function.)
16 //
17 //===----------------------------------------------------------------------===//
18 
19 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
20 #include "clang/StaticAnalyzer/Core/Checker.h"
21 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
25 #include "llvm/ADT/SmallVector.h"
26 #include "llvm/Support/FormatVariadic.h"
27 #include <optional>
28 
29 using namespace clang;
30 using namespace ento;
31 using llvm::formatv;
32 
33 namespace {
34 class ReturnValueChecker : public Checker<check::PostCall> {
35 public:
36   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
37 
38 private:
39   const CallDescriptionSet Methods = {
40       // These are known in the LLVM project: 'Error()'
41       {CDM::CXXMethod, {"ARMAsmParser", "Error"}},
42       {CDM::CXXMethod, {"HexagonAsmParser", "Error"}},
43       {CDM::CXXMethod, {"LLLexer", "Error"}},
44       {CDM::CXXMethod, {"LLParser", "Error"}},
45       {CDM::CXXMethod, {"MCAsmParser", "Error"}},
46       {CDM::CXXMethod, {"MCAsmParserExtension", "Error"}},
47       {CDM::CXXMethod, {"TGParser", "Error"}},
48       {CDM::CXXMethod, {"X86AsmParser", "Error"}},
49       // 'TokError()'
50       {CDM::CXXMethod, {"LLParser", "TokError"}},
51       {CDM::CXXMethod, {"MCAsmParser", "TokError"}},
52       {CDM::CXXMethod, {"MCAsmParserExtension", "TokError"}},
53       {CDM::CXXMethod, {"TGParser", "TokError"}},
54       // 'error()'
55       {CDM::CXXMethod, {"MIParser", "error"}},
56       {CDM::CXXMethod, {"WasmAsmParser", "error"}},
57       {CDM::CXXMethod, {"WebAssemblyAsmParser", "error"}},
58       // Other
59       {CDM::CXXMethod, {"AsmParser", "printError"}}};
60 };
61 } // namespace
62 
getName(const CallEvent & Call)63 static std::string getName(const CallEvent &Call) {
64   std::string Name;
65   if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl()))
66     if (const CXXRecordDecl *RD = MD->getParent())
67       Name += RD->getNameAsString() + "::";
68 
69   Name += Call.getCalleeIdentifier()->getName();
70   return Name;
71 }
72 
checkPostCall(const CallEvent & Call,CheckerContext & C) const73 void ReturnValueChecker::checkPostCall(const CallEvent &Call,
74                                        CheckerContext &C) const {
75   if (!Methods.contains(Call))
76     return;
77 
78   auto ReturnV = Call.getReturnValue().getAs<DefinedOrUnknownSVal>();
79 
80   if (!ReturnV)
81     return;
82 
83   ProgramStateRef State = C.getState();
84   if (ProgramStateRef StTrue = State->assume(*ReturnV, true)) {
85     // The return value can be true, so transition to a state where it's true.
86     std::string Msg =
87         formatv("'{0}' returns true (by convention)", getName(Call));
88     C.addTransition(StTrue, C.getNoteTag(Msg, /*IsPrunable=*/true));
89     return;
90   }
91   // Paranoia: if the return value is known to be false (which is highly
92   // unlikely, it's easy to ensure that the method always returns true), then
93   // produce a note that highlights that this unusual situation.
94   // Note that this checker is 'hidden' so it cannot produce a bug report.
95   std::string Msg = formatv("'{0}' returned false, breaking the convention "
96                             "that it always returns true",
97                             getName(Call));
98   C.addTransition(State, C.getNoteTag(Msg, /*IsPrunable=*/true));
99 }
100 
registerReturnValueChecker(CheckerManager & Mgr)101 void ento::registerReturnValueChecker(CheckerManager &Mgr) {
102   Mgr.registerChecker<ReturnValueChecker>();
103 }
104 
shouldRegisterReturnValueChecker(const CheckerManager & mgr)105 bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) {
106   return true;
107 }
108