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/Support/FormatVariadic.h"
26#include <optional>
27
28using namespace clang;
29using namespace ento;
30using llvm::formatv;
31
32namespace {
33class ReturnValueChecker : public Checker<check::PostCall> {
34public:
35 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
36
37private:
38 const CallDescriptionSet Methods = {
39 // These are known in the LLVM project: 'Error()'
40 {CDM::CXXMethod, {"ARMAsmParser", "Error"}},
41 {CDM::CXXMethod, {"HexagonAsmParser", "Error"}},
42 {CDM::CXXMethod, {"LLLexer", "Error"}},
43 {CDM::CXXMethod, {"LLParser", "Error"}},
44 {CDM::CXXMethod, {"MCAsmParser", "Error"}},
45 {CDM::CXXMethod, {"MCAsmParserExtension", "Error"}},
46 {CDM::CXXMethod, {"TGParser", "Error"}},
47 {CDM::CXXMethod, {"X86AsmParser", "Error"}},
48 // 'TokError()'
49 {CDM::CXXMethod, {"LLParser", "TokError"}},
50 {CDM::CXXMethod, {"MCAsmParser", "TokError"}},
51 {CDM::CXXMethod, {"MCAsmParserExtension", "TokError"}},
52 {CDM::CXXMethod, {"TGParser", "TokError"}},
53 // 'error()'
54 {CDM::CXXMethod, {"MIParser", "error"}},
55 {CDM::CXXMethod, {"WasmAsmParser", "error"}},
56 {CDM::CXXMethod, {"WebAssemblyAsmParser", "error"}},
57 // Other
58 {CDM::CXXMethod, {"AsmParser", "printError"}}};
59};
60} // namespace
61
62static std::string getFunctionName(const CallEvent &Call) {
63 std::string Name;
64 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: Call.getDecl()))
65 if (const CXXRecordDecl *RD = MD->getParent())
66 Name += RD->getNameAsString() + "::";
67
68 Name += Call.getCalleeIdentifier()->getName();
69 return Name;
70}
71
72void ReturnValueChecker::checkPostCall(const CallEvent &Call,
73 CheckerContext &C) const {
74 if (!Methods.contains(Call))
75 return;
76
77 auto ReturnV = Call.getReturnValue().getAs<DefinedOrUnknownSVal>();
78
79 if (!ReturnV)
80 return;
81
82 ProgramStateRef State = C.getState();
83 if (ProgramStateRef StTrue = State->assume(Cond: *ReturnV, Assumption: true)) {
84 // The return value can be true, so transition to a state where it's true.
85 std::string Msg =
86 formatv(Fmt: "'{0}' returns true (by convention)", Vals: getFunctionName(Call));
87 C.addTransition(State: StTrue, Tag: C.getNoteTag(Note: Msg, /*IsPrunable=*/true));
88 return;
89 }
90 // Paranoia: if the return value is known to be false (which is highly
91 // unlikely, it's easy to ensure that the method always returns true), then
92 // produce a note that highlights that this unusual situation.
93 // Note that this checker is 'hidden' so it cannot produce a bug report.
94 std::string Msg = formatv(Fmt: "'{0}' returned false, breaking the convention "
95 "that it always returns true",
96 Vals: getFunctionName(Call));
97 C.addTransition(State, Tag: C.getNoteTag(Note: Msg, /*IsPrunable=*/true));
98}
99
100void ento::registerReturnValueChecker(CheckerManager &Mgr) {
101 Mgr.registerChecker<ReturnValueChecker>();
102}
103
104bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) {
105 return true;
106}
107