#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
#include "ModelInjector.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CallGraph.h"
#include "clang/Analysis/CodeInjector.h"
#include "clang/Analysis/MacroExpansionContext.h"
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/CrossTU/CrossTranslationUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <queue>
#include <utility>
using namespace clang;
using namespace ento;
#define DEBUG_TYPE "AnalysisConsumer"
STATISTIC(NumFunctionTopLevel, "The # of functions at top level.");
STATISTIC(NumFunctionsAnalyzed,
"The # of functions and blocks analyzed (as top level "
"with inlining turned on).");
STATISTIC(NumBlocksInAnalyzedFunctions,
"The # of basic blocks in the analyzed functions.");
STATISTIC(NumVisitedBlocksInAnalyzedFunctions,
"The # of visited basic blocks in the analyzed functions.");
STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks.");
STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function.");
namespace {
class AnalysisConsumer : public AnalysisASTConsumer,
public RecursiveASTVisitor<AnalysisConsumer> {
enum {
AM_None = 0,
AM_Syntax = 0x1,
AM_Path = 0x2
};
typedef unsigned AnalysisMode;
AnalysisMode RecVisitorMode;
BugReporter *RecVisitorBR;
std::vector<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns;
public:
ASTContext *Ctx;
Preprocessor &PP;
const std::string OutDir;
AnalyzerOptionsRef Opts;
ArrayRef<std::string> Plugins;
CodeInjector *Injector;
cross_tu::CrossTranslationUnitContext CTU;
SetOfDecls LocalTUDecls;
MacroExpansionContext MacroExpansions;
PathDiagnosticConsumers PathConsumers;
StoreManagerCreator CreateStoreMgr;
ConstraintManagerCreator CreateConstraintMgr;
std::unique_ptr<CheckerManager> checkerMgr;
std::unique_ptr<AnalysisManager> Mgr;
std::unique_ptr<llvm::TimerGroup> AnalyzerTimers;
std::unique_ptr<llvm::Timer> SyntaxCheckTimer;
std::unique_ptr<llvm::Timer> ExprEngineTimer;
std::unique_ptr<llvm::Timer> BugReporterTimer;
FunctionSummariesTy FunctionSummaries;
AnalysisConsumer(CompilerInstance &CI, const std::string &outdir,
AnalyzerOptionsRef opts, ArrayRef<std::string> plugins,
CodeInjector *injector)
: RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr),
PP(CI.getPreprocessor()), OutDir(outdir), Opts(std::move(opts)),
Plugins(plugins), Injector(injector), CTU(CI),
MacroExpansions(CI.getLangOpts()) {
DigestAnalyzerOptions();
if (Opts->AnalyzerDisplayProgress || Opts->PrintStats ||
Opts->ShouldSerializeStats) {
AnalyzerTimers = std::make_unique<llvm::TimerGroup>(
"analyzer", "Analyzer timers");
SyntaxCheckTimer = std::make_unique<llvm::Timer>(
"syntaxchecks", "Syntax-based analysis time", *AnalyzerTimers);
ExprEngineTimer = std::make_unique<llvm::Timer>(
"exprengine", "Path exploration time", *AnalyzerTimers);
BugReporterTimer = std::make_unique<llvm::Timer>(
"bugreporter", "Path-sensitive report post-processing time",
*AnalyzerTimers);
}
if (Opts->PrintStats || Opts->ShouldSerializeStats) {
llvm::EnableStatistics( false);
}
if (Opts->ShouldDisplayMacroExpansions)
MacroExpansions.registerForPreprocessor(PP);
}
~AnalysisConsumer() override {
if (Opts->PrintStats) {
llvm::PrintStatistics();
}
}
void DigestAnalyzerOptions() {
switch (Opts->AnalysisDiagOpt) {
case PD_NONE:
break;
#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \
case PD_##NAME: \
CREATEFN(Opts->getDiagOpts(), PathConsumers, OutDir, PP, CTU, \
MacroExpansions); \
break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
default:
llvm_unreachable("Unknown analyzer output type!");
}
CreateStoreMgr = &CreateRegionStoreManager;
switch (Opts->AnalysisConstraintsOpt) {
default:
llvm_unreachable("Unknown constraint manager.");
#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \
case NAME##Model: CreateConstraintMgr = CREATEFN; break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
}
}
void DisplayTime(llvm::TimeRecord &Time) {
if (!Opts->AnalyzerDisplayProgress) {
return;
}
llvm::errs() << " : " << llvm::format("%1.1f", Time.getWallTime() * 1000)
<< " ms\n";
}
void DisplayFunction(const Decl *D, AnalysisMode Mode,
ExprEngine::InliningModes IMode) {
if (!Opts->AnalyzerDisplayProgress)
return;
SourceManager &SM = Mgr->getASTContext().getSourceManager();
PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
if (Loc.isValid()) {
llvm::errs() << "ANALYZE";
if (Mode == AM_Syntax)
llvm::errs() << " (Syntax)";
else if (Mode == AM_Path) {
llvm::errs() << " (Path, ";
switch (IMode) {
case ExprEngine::Inline_Minimal:
llvm::errs() << " Inline_Minimal";
break;
case ExprEngine::Inline_Regular:
llvm::errs() << " Inline_Regular";
break;
}
llvm::errs() << ")";
} else
assert(Mode == (AM_Syntax | AM_Path) && "Unexpected mode!");
llvm::errs() << ": " << Loc.getFilename() << ' '
<< AnalysisDeclContext::getFunctionName(D);
}
}
void Initialize(ASTContext &Context) override {
Ctx = &Context;
checkerMgr = std::make_unique<CheckerManager>(*Ctx, *Opts, PP, Plugins,
CheckerRegistrationFns);
Mgr = std::make_unique<AnalysisManager>(*Ctx, PP, PathConsumers,
CreateStoreMgr, CreateConstraintMgr,
checkerMgr.get(), *Opts, Injector);
}
bool HandleTopLevelDecl(DeclGroupRef D) override;
void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override;
void HandleTranslationUnit(ASTContext &C) override;
ExprEngine::InliningModes
getInliningModeForFunction(const Decl *D, const SetOfConstDecls &Visited);
void HandleDeclsCallGraph(const unsigned LocalTUDeclsSize);
void HandleCode(Decl *D, AnalysisMode Mode,
ExprEngine::InliningModes IMode = ExprEngine::Inline_Minimal,
SetOfConstDecls *VisitedCallees = nullptr);
void RunPathSensitiveChecks(Decl *D,
ExprEngine::InliningModes IMode,
SetOfConstDecls *VisitedCallees);
bool shouldWalkTypesOfTypeLocs() const { return false; }
bool VisitDecl(Decl *D) {
AnalysisMode Mode = getModeForDecl(D, RecVisitorMode);
if (Mode & AM_Syntax) {
if (SyntaxCheckTimer)
SyntaxCheckTimer->startTimer();
checkerMgr->runCheckersOnASTDecl(D, *Mgr, *RecVisitorBR);
if (SyntaxCheckTimer)
SyntaxCheckTimer->stopTimer();
}
return true;
}
bool VisitVarDecl(VarDecl *VD) {
if (!Opts->IsNaiveCTUEnabled)
return true;
if (VD->hasExternalStorage() || VD->isStaticDataMember()) {
if (!cross_tu::shouldImport(VD, *Ctx))
return true;
} else {
return true;
}
if (VD->getAnyInitializer())
return true;
llvm::Expected<const VarDecl *> CTUDeclOrError =
CTU.getCrossTUDefinition(VD, Opts->CTUDir, Opts->CTUIndexName,
Opts->DisplayCTUProgress);
if (!CTUDeclOrError) {
handleAllErrors(CTUDeclOrError.takeError(),
[&](const cross_tu::IndexError &IE) {
CTU.emitCrossTUDiagnostics(IE);
});
}
return true;
}
bool VisitFunctionDecl(FunctionDecl *FD) {
IdentifierInfo *II = FD->getIdentifier();
if (II && II->getName().startswith("__inline"))
return true;
if (FD->isThisDeclarationADefinition() &&
!FD->isDependentContext()) {
assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
HandleCode(FD, RecVisitorMode);
}
return true;
}
bool VisitObjCMethodDecl(ObjCMethodDecl *MD) {
if (MD->isThisDeclarationADefinition()) {
assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
HandleCode(MD, RecVisitorMode);
}
return true;
}
bool VisitBlockDecl(BlockDecl *BD) {
if (BD->hasBody()) {
assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
if (!BD->isDependentContext()) {
HandleCode(BD, RecVisitorMode);
}
}
return true;
}
void AddDiagnosticConsumer(PathDiagnosticConsumer *Consumer) override {
PathConsumers.push_back(Consumer);
}
void AddCheckerRegistrationFn(std::function<void(CheckerRegistry&)> Fn) override {
CheckerRegistrationFns.push_back(std::move(Fn));
}
private:
void storeTopLevelDecls(DeclGroupRef DG);
AnalysisMode getModeForDecl(Decl *D, AnalysisMode Mode);
void runAnalysisOnTranslationUnit(ASTContext &C);
void reportAnalyzerProgress(StringRef S);
}; }
bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) {
storeTopLevelDecls(DG);
return true;
}
void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
storeTopLevelDecls(DG);
}
void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) {
for (auto &I : DG) {
if (isa<ObjCMethodDecl>(I))
continue;
LocalTUDecls.push_back(I);
}
}
static bool shouldSkipFunction(const Decl *D,
const SetOfConstDecls &Visited,
const SetOfConstDecls &VisitedAsTopLevel) {
if (VisitedAsTopLevel.count(D))
return true;
if (const auto *CD = dyn_cast<CXXConstructorDecl>(D))
if (CD->isInheritingConstructor())
return true;
if (isa<ObjCMethodDecl>(D))
return false;
if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
if (MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator())
return false;
}
return Visited.count(D);
}
ExprEngine::InliningModes
AnalysisConsumer::getInliningModeForFunction(const Decl *D,
const SetOfConstDecls &Visited) {
if (Visited.count(D) && isa<ObjCMethodDecl>(D)) {
const ObjCMethodDecl *ObjCM = cast<ObjCMethodDecl>(D);
if (ObjCM->getMethodFamily() != OMF_init)
return ExprEngine::Inline_Minimal;
}
return ExprEngine::Inline_Regular;
}
void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
CallGraph CG;
for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
CG.addToCallGraph(LocalTUDecls[i]);
}
SetOfConstDecls Visited;
SetOfConstDecls VisitedAsTopLevel;
llvm::ReversePostOrderTraversal<clang::CallGraph*> RPOT(&CG);
for (auto &N : RPOT) {
NumFunctionTopLevel++;
Decl *D = N->getDecl();
if (!D)
continue;
if (shouldSkipFunction(D, Visited, VisitedAsTopLevel))
continue;
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
FD->hasBody(FD);
if (CTU.isImportedAsNew(FD))
continue;
}
SetOfConstDecls VisitedCallees;
HandleCode(D, AM_Path, getInliningModeForFunction(D, Visited),
(Mgr->options.InliningMode == All ? nullptr : &VisitedCallees));
for (const Decl *Callee : VisitedCallees)
Visited.insert(isa<ObjCMethodDecl>(Callee) ? Callee
: Callee->getCanonicalDecl());
VisitedAsTopLevel.insert(D);
}
}
static bool fileContainsString(StringRef Substring, ASTContext &C) {
const SourceManager &SM = C.getSourceManager();
FileID FID = SM.getMainFileID();
StringRef Buffer = SM.getBufferOrFake(FID).getBuffer();
return Buffer.contains(Substring);
}
static void reportAnalyzerFunctionMisuse(const AnalyzerOptions &Opts,
const ASTContext &Ctx) {
llvm::errs() << "Every top-level function was skipped.\n";
if (!Opts.AnalyzerDisplayProgress)
llvm::errs() << "Pass the -analyzer-display-progress for tracking which "
"functions are analyzed.\n";
bool HasBrackets =
Opts.AnalyzeSpecificFunction.find("(") != std::string::npos;
if (Ctx.getLangOpts().CPlusPlus && !HasBrackets) {
llvm::errs()
<< "For analyzing C++ code you need to pass the function parameter "
"list: -analyze-function=\"foobar(int, _Bool)\"\n";
} else if (!Ctx.getLangOpts().CPlusPlus && HasBrackets) {
llvm::errs() << "For analyzing C code you shouldn't pass the function "
"parameter list, only the name of the function: "
"-analyze-function=foobar\n";
}
}
void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) {
BugReporter BR(*Mgr);
TranslationUnitDecl *TU = C.getTranslationUnitDecl();
if (SyntaxCheckTimer)
SyntaxCheckTimer->startTimer();
checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
if (SyntaxCheckTimer)
SyntaxCheckTimer->stopTimer();
RecVisitorMode = AM_Syntax;
if (!Mgr->shouldInlineCall())
RecVisitorMode |= AM_Path;
RecVisitorBR = &BR;
const unsigned LocalTUDeclsSize = LocalTUDecls.size();
for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
TraverseDecl(LocalTUDecls[i]);
}
if (Mgr->shouldInlineCall())
HandleDeclsCallGraph(LocalTUDeclsSize);
checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR);
BR.FlushReports();
RecVisitorBR = nullptr;
if (!Opts->AnalyzeSpecificFunction.empty() && NumFunctionsAnalyzed == 0)
reportAnalyzerFunctionMisuse(*Opts, *Ctx);
}
void AnalysisConsumer::reportAnalyzerProgress(StringRef S) {
if (Opts->AnalyzerDisplayProgress)
llvm::errs() << S;
}
void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
DiagnosticsEngine &Diags = PP.getDiagnostics();
if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
return;
const auto DiagFlusherScopeExit =
llvm::make_scope_exit([this] { Mgr.reset(); });
if (Opts->ShouldIgnoreBisonGeneratedFiles &&
fileContainsString("/* A Bison parser, made by", C)) {
reportAnalyzerProgress("Skipping bison-generated file\n");
return;
}
if (Opts->ShouldIgnoreFlexGeneratedFiles &&
fileContainsString("/* A lexical scanner generated by flex", C)) {
reportAnalyzerProgress("Skipping flex-generated file\n");
return;
}
if (Opts->DisableAllCheckers) {
reportAnalyzerProgress("All checks are disabled using a supplied option\n");
return;
}
runAnalysisOnTranslationUnit(C);
NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks();
NumVisitedBlocksInAnalyzedFunctions =
FunctionSummaries.getTotalNumVisitedBasicBlocks();
if (NumBlocksInAnalyzedFunctions > 0)
PercentReachableBlocks =
(FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) /
NumBlocksInAnalyzedFunctions;
}
AnalysisConsumer::AnalysisMode
AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) {
if (!Opts->AnalyzeSpecificFunction.empty() &&
AnalysisDeclContext::getFunctionName(D) != Opts->AnalyzeSpecificFunction)
return AM_None;
if (Opts->AnalyzeAll)
return Mode;
const SourceManager &SM = Ctx->getSourceManager();
const SourceLocation Loc = [&SM](Decl *D) -> SourceLocation {
const Stmt *Body = D->getBody();
SourceLocation SL = Body ? Body->getBeginLoc() : D->getLocation();
return SM.getExpansionLoc(SL);
}(D);
if (Loc.isInvalid() || SM.isInSystemHeader(Loc))
return AM_None;
if (!Mgr->isInCodeFile(Loc))
return Mode & ~AM_Path;
return Mode;
}
void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
ExprEngine::InliningModes IMode,
SetOfConstDecls *VisitedCallees) {
if (!D->hasBody())
return;
Mode = getModeForDecl(D, Mode);
if (Mode == AM_None)
return;
Mgr->ClearContexts();
if (Mgr->getAnalysisDeclContext(D)->isBodyAutosynthesized())
return;
CFG *DeclCFG = Mgr->getCFG(D);
if (DeclCFG)
MaxCFGSize.updateMax(DeclCFG->size());
DisplayFunction(D, Mode, IMode);
BugReporter BR(*Mgr);
if (Mode & AM_Syntax) {
llvm::TimeRecord CheckerStartTime;
if (SyntaxCheckTimer) {
CheckerStartTime = SyntaxCheckTimer->getTotalTime();
SyntaxCheckTimer->startTimer();
}
checkerMgr->runCheckersOnASTBody(D, *Mgr, BR);
if (SyntaxCheckTimer) {
SyntaxCheckTimer->stopTimer();
llvm::TimeRecord CheckerEndTime = SyntaxCheckTimer->getTotalTime();
CheckerEndTime -= CheckerStartTime;
DisplayTime(CheckerEndTime);
}
}
BR.FlushReports();
if ((Mode & AM_Path) && checkerMgr->hasPathSensitiveCheckers()) {
RunPathSensitiveChecks(D, IMode, VisitedCallees);
if (IMode != ExprEngine::Inline_Minimal)
NumFunctionsAnalyzed++;
}
}
void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
ExprEngine::InliningModes IMode,
SetOfConstDecls *VisitedCallees) {
if (!Mgr->getCFG(D))
return;
if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>())
return;
ExprEngine Eng(CTU, *Mgr, VisitedCallees, &FunctionSummaries, IMode);
llvm::TimeRecord ExprEngineStartTime;
if (ExprEngineTimer) {
ExprEngineStartTime = ExprEngineTimer->getTotalTime();
ExprEngineTimer->startTimer();
}
Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D),
Mgr->options.MaxNodesPerTopLevelFunction);
if (ExprEngineTimer) {
ExprEngineTimer->stopTimer();
llvm::TimeRecord ExprEngineEndTime = ExprEngineTimer->getTotalTime();
ExprEngineEndTime -= ExprEngineStartTime;
DisplayTime(ExprEngineEndTime);
}
if (!Mgr->options.DumpExplodedGraphTo.empty())
Eng.DumpGraph(Mgr->options.TrimGraph, Mgr->options.DumpExplodedGraphTo);
if (Mgr->options.visualizeExplodedGraphWithGraphViz)
Eng.ViewGraph(Mgr->options.TrimGraph);
if (BugReporterTimer)
BugReporterTimer->startTimer();
Eng.getBugReporter().FlushReports();
if (BugReporterTimer)
BugReporterTimer->stopTimer();
}
std::unique_ptr<AnalysisASTConsumer>
ento::CreateAnalysisConsumer(CompilerInstance &CI) {
CI.getPreprocessor().getDiagnostics().setWarningsAsErrors(false);
AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts();
bool hasModelPath = analyzerOpts->Config.count("model-path") > 0;
return std::make_unique<AnalysisConsumer>(
CI, CI.getFrontendOpts().OutputFile, analyzerOpts,
CI.getFrontendOpts().Plugins,
hasModelPath ? new ModelInjector(CI) : nullptr);
}