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 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 73 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 101 void ento::registerReturnValueChecker(CheckerManager &Mgr) { 102 Mgr.registerChecker<ReturnValueChecker>(); 103 } 104 105 bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) { 106 return true; 107 } 108