Skip to content

[analyzer] Delay the checker constructions after parsing #127409

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 32 additions & 29 deletions clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,28 @@ enum class OpenVariant {
OpenAt
};

static std::optional<int> getCreateFlagValue(const ASTContext &Ctx,
const Preprocessor &PP) {
std::optional<int> MacroVal = tryExpandAsInteger("O_CREAT", PP);
if (MacroVal.has_value())
return MacroVal;

// If we failed, fall-back to known values.
if (Ctx.getTargetInfo().getTriple().getVendor() == llvm::Triple::Apple)
return {0x0200};
return MacroVal;
}

Copy link
Collaborator

@balazske balazske Feb 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tryExpandAsInteger could have a version which returns a non-optional (or optional) value and takes a default value or a lambda for the default value. It could be used instead of this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd refrain from generalizing right away. If we see this pattern appear again, we can think about concrete steps reducing the code duplication. So far I don't think it's the time.

namespace {

class UnixAPIMisuseChecker
: public Checker<check::PreCall, check::ASTDecl<TranslationUnitDecl>> {
class UnixAPIMisuseChecker : public Checker<check::PreCall> {
const BugType BT_open{this, "Improper use of 'open'", categories::UnixAPI};
const BugType BT_getline{this, "Improper use of getdelim",
categories::UnixAPI};
const BugType BT_pthreadOnce{this, "Improper use of 'pthread_once'",
categories::UnixAPI};
const BugType BT_ArgumentNull{this, "NULL pointer", categories::UnixAPI};
mutable std::optional<uint64_t> Val_O_CREAT;
const std::optional<int> Val_O_CREAT;

ProgramStateRef
EnsurePtrNotNull(SVal PtrVal, const Expr *PtrExpr, CheckerContext &C,
Expand All @@ -63,6 +74,9 @@ class UnixAPIMisuseChecker
const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const;

public:
UnixAPIMisuseChecker(const ASTContext &Ctx, const Preprocessor &PP)
: Val_O_CREAT(getCreateFlagValue(Ctx, PP)) {}

void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager &Mgr,
BugReporter &BR) const;

Expand Down Expand Up @@ -134,20 +148,6 @@ ProgramStateRef UnixAPIMisuseChecker::EnsurePtrNotNull(
return PtrNotNull;
}

void UnixAPIMisuseChecker::checkASTDecl(const TranslationUnitDecl *TU,
AnalysisManager &Mgr,
BugReporter &) const {
// The definition of O_CREAT is platform specific.
// Try to get the macro value from the preprocessor.
Val_O_CREAT = tryExpandAsInteger("O_CREAT", Mgr.getPreprocessor());
// If we failed, fall-back to known values.
if (!Val_O_CREAT) {
if (TU->getASTContext().getTargetInfo().getTriple().getVendor() ==
llvm::Triple::Apple)
Val_O_CREAT = 0x0200;
}
}

//===----------------------------------------------------------------------===//
// "open" (man 2 open)
//===----------------------------------------------------------------------===/
Expand Down Expand Up @@ -262,7 +262,7 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
return;
}

if (!Val_O_CREAT) {
if (!Val_O_CREAT.has_value()) {
return;
}

Expand All @@ -276,7 +276,7 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
}
NonLoc oflags = V.castAs<NonLoc>();
NonLoc ocreateFlag = C.getSValBuilder()
.makeIntVal(*Val_O_CREAT, oflagsEx->getType())
.makeIntVal(Val_O_CREAT.value(), oflagsEx->getType())
.castAs<NonLoc>();
SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
oflags, ocreateFlag,
Expand Down Expand Up @@ -621,14 +621,17 @@ void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
// Registration.
//===----------------------------------------------------------------------===//

#define REGISTER_CHECKER(CHECKERNAME) \
void ento::register##CHECKERNAME(CheckerManager &mgr) { \
mgr.registerChecker<CHECKERNAME>(); \
} \
\
bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) { \
return true; \
}
void ento::registerUnixAPIMisuseChecker(CheckerManager &Mgr) {
Mgr.registerChecker<UnixAPIMisuseChecker>(Mgr.getASTContext(),
Mgr.getPreprocessor());
}
bool ento::shouldRegisterUnixAPIMisuseChecker(const CheckerManager &Mgr) {
return true;
}

REGISTER_CHECKER(UnixAPIMisuseChecker)
REGISTER_CHECKER(UnixAPIPortabilityChecker)
void ento::registerUnixAPIPortabilityChecker(CheckerManager &Mgr) {
Mgr.registerChecker<UnixAPIPortabilityChecker>();
}
bool ento::shouldRegisterUnixAPIPortabilityChecker(const CheckerManager &Mgr) {
return true;
}
18 changes: 8 additions & 10 deletions clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,16 +224,6 @@ class AnalysisConsumer : public AnalysisASTConsumer,
}
}

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);
}

/// Store the top level decls in the set to be processed later on.
/// (Doing this pre-processing avoids deserialization of data from PCH.)
bool HandleTopLevelDecl(DeclGroupRef D) override;
Expand Down Expand Up @@ -615,6 +605,14 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
return;

Ctx = &C;
checkerMgr = std::make_unique<CheckerManager>(*Ctx, Opts, PP, Plugins,
CheckerRegistrationFns);

Mgr = std::make_unique<AnalysisManager>(*Ctx, PP, PathConsumers,
CreateStoreMgr, CreateConstraintMgr,
checkerMgr.get(), Opts, Injector);

// Explicitly destroy the PathDiagnosticConsumer. This will flush its output.
// FIXME: This should be replaced with something that doesn't rely on
// side-effects in PathDiagnosticConsumer's destructor. This is required when
Expand Down