| //===- ReturnValueChecker - Check methods always returning true -*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This defines ReturnValueChecker, which models a very specific coding |
| // convention within the LLVM/Clang codebase: there several classes that have |
| // Error() methods which always return true. |
| // This checker was introduced to eliminate false positives caused by this |
| // peculiar "always returns true" invariant. (Normally, the analyzer assumes |
| // that a function returning `bool` can return both `true` and `false`, because |
| // otherwise it could've been a `void` function.) |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
| #include "clang/StaticAnalyzer/Core/Checker.h" |
| #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
| #include "llvm/Support/FormatVariadic.h" |
| #include <optional> |
| |
| using namespace clang; |
| using namespace ento; |
| using llvm::formatv; |
| |
| namespace { |
| class ReturnValueChecker : public Checker<check::PostCall> { |
| public: |
| void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
| |
| private: |
| const CallDescriptionSet Methods = { |
| // These are known in the LLVM project: 'Error()' |
| {CDM::CXXMethod, {"ARMAsmParser", "Error"}}, |
| {CDM::CXXMethod, {"HexagonAsmParser", "Error"}}, |
| {CDM::CXXMethod, {"LLLexer", "Error"}}, |
| {CDM::CXXMethod, {"LLParser", "Error"}}, |
| {CDM::CXXMethod, {"MCAsmParser", "Error"}}, |
| {CDM::CXXMethod, {"MCAsmParserExtension", "Error"}}, |
| {CDM::CXXMethod, {"TGParser", "Error"}}, |
| {CDM::CXXMethod, {"X86AsmParser", "Error"}}, |
| // 'TokError()' |
| {CDM::CXXMethod, {"LLParser", "TokError"}}, |
| {CDM::CXXMethod, {"MCAsmParser", "TokError"}}, |
| {CDM::CXXMethod, {"MCAsmParserExtension", "TokError"}}, |
| {CDM::CXXMethod, {"TGParser", "TokError"}}, |
| // 'error()' |
| {CDM::CXXMethod, {"MIParser", "error"}}, |
| {CDM::CXXMethod, {"WasmAsmParser", "error"}}, |
| {CDM::CXXMethod, {"WebAssemblyAsmParser", "error"}}, |
| // Other |
| {CDM::CXXMethod, {"AsmParser", "printError"}}}; |
| }; |
| } // namespace |
| |
| static std::string getFunctionName(const CallEvent &Call) { |
| std::string Name; |
| if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl())) |
| if (const CXXRecordDecl *RD = MD->getParent()) |
| Name += RD->getNameAsString() + "::"; |
| |
| Name += Call.getCalleeIdentifier()->getName(); |
| return Name; |
| } |
| |
| void ReturnValueChecker::checkPostCall(const CallEvent &Call, |
| CheckerContext &C) const { |
| if (!Methods.contains(Call)) |
| return; |
| |
| auto ReturnV = Call.getReturnValue().getAs<DefinedOrUnknownSVal>(); |
| |
| if (!ReturnV) |
| return; |
| |
| ProgramStateRef State = C.getState(); |
| if (ProgramStateRef StTrue = State->assume(*ReturnV, true)) { |
| // The return value can be true, so transition to a state where it's true. |
| std::string Msg = |
| formatv("'{0}' returns true (by convention)", getFunctionName(Call)); |
| C.addTransition(StTrue, C.getNoteTag(Msg, /*IsPrunable=*/true)); |
| return; |
| } |
| // Paranoia: if the return value is known to be false (which is highly |
| // unlikely, it's easy to ensure that the method always returns true), then |
| // produce a note that highlights that this unusual situation. |
| // Note that this checker is 'hidden' so it cannot produce a bug report. |
| std::string Msg = formatv("'{0}' returned false, breaking the convention " |
| "that it always returns true", |
| getFunctionName(Call)); |
| C.addTransition(State, C.getNoteTag(Msg, /*IsPrunable=*/true)); |
| } |
| |
| void ento::registerReturnValueChecker(CheckerManager &Mgr) { |
| Mgr.registerChecker<ReturnValueChecker>(); |
| } |
| |
| bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) { |
| return true; |
| } |