#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/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
using namespace clang;
using namespace ento;
namespace {
class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> {
public:
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
private:
const CallDescriptionMap<bool> CDM = {
{{{"ARMAsmParser", "Error"}}, true},
{{{"HexagonAsmParser", "Error"}}, true},
{{{"LLLexer", "Error"}}, true},
{{{"LLParser", "Error"}}, true},
{{{"MCAsmParser", "Error"}}, true},
{{{"MCAsmParserExtension", "Error"}}, true},
{{{"TGParser", "Error"}}, true},
{{{"X86AsmParser", "Error"}}, true},
{{{"LLParser", "TokError"}}, true},
{{{"MCAsmParser", "TokError"}}, true},
{{{"MCAsmParserExtension", "TokError"}}, true},
{{{"TGParser", "TokError"}}, true},
{{{"MIParser", "error"}}, true},
{{{"WasmAsmParser", "error"}}, true},
{{{"WebAssemblyAsmParser", "error"}}, true},
{{{"AsmParser", "printError"}}, true}};
};
}
static std::string getName(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;
}
static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
CheckerContext &C) {
auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
if (!ReturnDV)
return None;
if (ExpectedValue)
return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
return C.getState()->isNull(*ReturnDV).isConstrainedFalse();
}
void ReturnValueChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
const bool *RawExpectedValue = CDM.lookup(Call);
if (!RawExpectedValue)
return;
SVal ReturnV = Call.getReturnValue();
bool ExpectedValue = *RawExpectedValue;
Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
if (!IsInvariantBreak)
return;
if (*IsInvariantBreak)
return;
std::string Name = getName(Call);
const NoteTag *CallTag = C.getNoteTag(
[Name, ExpectedValue](PathSensitiveBugReport &) -> std::string {
SmallString<128> Msg;
llvm::raw_svector_ostream Out(Msg);
Out << '\'' << Name << "' returns "
<< (ExpectedValue ? "true" : "false");
return std::string(Out.str());
},
true);
ProgramStateRef State = C.getState();
State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue);
C.addTransition(State, CallTag);
}
void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
CheckerContext &C) const {
if (!RS || !RS->getRetValue())
return;
const StackFrameContext *SFC = C.getStackFrame();
if (C.getStackFrame()->inTopFrame())
return;
ProgramStateRef State = C.getState();
CallEventManager &CMgr = C.getStateManager().getCallEventManager();
CallEventRef<> Call = CMgr.getCaller(SFC, State);
if (!Call)
return;
const bool *RawExpectedValue = CDM.lookup(*Call);
if (!RawExpectedValue)
return;
SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
bool ExpectedValue = *RawExpectedValue;
Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
if (!IsInvariantBreak)
return;
if (!*IsInvariantBreak)
return;
std::string Name = getName(*Call);
const NoteTag *CallTag = C.getNoteTag(
[Name, ExpectedValue](BugReport &BR) -> std::string {
SmallString<128> Msg;
llvm::raw_svector_ostream Out(Msg);
Out << '\'' << Name << "' returns "
<< (ExpectedValue ? "false" : "true");
return std::string(Out.str());
},
false);
C.addTransition(State, CallTag);
}
void ento::registerReturnValueChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ReturnValueChecker>();
}
bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) {
return true;
}