107 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This defines ReturnValueChecker, which models a very specific coding10// convention within the LLVM/Clang codebase: there several classes that have11// Error() methods which always return true.12// This checker was introduced to eliminate false positives caused by this13// peculiar "always returns true" invariant. (Normally, the analyzer assumes14// that a function returning `bool` can return both `true` and `false`, because15// 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 // Other58 {CDM::CXXMethod, {"AsmParser", "printError"}}};59};60} // namespace61 62static std::string getFunctionName(const CallEvent &Call) {63 std::string Name;64 if (const auto *MD = dyn_cast<CXXMethodDecl>(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(*ReturnV, true)) {84 // The return value can be true, so transition to a state where it's true.85 std::string Msg =86 formatv("'{0}' returns true (by convention)", getFunctionName(Call));87 C.addTransition(StTrue, C.getNoteTag(Msg, /*IsPrunable=*/true));88 return;89 }90 // Paranoia: if the return value is known to be false (which is highly91 // unlikely, it's easy to ensure that the method always returns true), then92 // 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("'{0}' returned false, breaking the convention "95 "that it always returns true",96 getFunctionName(Call));97 C.addTransition(State, C.getNoteTag(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