Skip to content

Commit c19cacf

Browse files
authored
[clang][dataflow] Tighten checking for existence of a function body. (#78163)
In various places, we would previously call `FunctionDecl::hasBody()` (which checks whether any redeclaration of the function has a body, not necessarily the one on which `hasBody()` is being called). This is bug-prone, as a recent bug in Crubit's nullability checker has shown ([fix](google/crubit@4b01ed0), [fix for the fix](google/crubit@e0c5d8d)). Instead, we now use `FunctionDecl::doesThisDeclarationHaveABody()` which, as the name implies, checks whether the specific redeclaration it is being called on has a body. Alternatively, I considered being more lenient and "canonicalizing" to the `FunctionDecl` that has the body if the `FunctionDecl` being passed is a different redeclaration. However, this also risks hiding bugs: A caller might inadverently perform the analysis for all redeclarations of a function and end up duplicating work without realizing it. By accepting only the redeclaration that contains the body, we prevent this. I've checked, and all clients that I'm aware of do currently pass in the redeclaration that contains the function body. Typically this is because they use the `ast_matchers::hasBody()` matcher which, unlike `FunctionDecl::hasBody()`, only matches for the redeclaration containing the body.
1 parent 032c832 commit c19cacf

File tree

5 files changed

+8
-6
lines changed

5 files changed

+8
-6
lines changed

clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ namespace dataflow {
3232
class ControlFlowContext {
3333
public:
3434
/// Builds a ControlFlowContext from a `FunctionDecl`.
35-
/// `Func.hasBody()` must be true, and `Func.isTemplated()` must be false.
35+
/// `Func.doesThisDeclarationHaveABody()` must be true, and
36+
/// `Func.isTemplated()` must be false.
3637
static llvm::Expected<ControlFlowContext> build(const FunctionDecl &Func);
3738

3839
/// Builds a ControlFlowContext from an AST node. `D` is the function in which

clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ class Environment {
172172
///
173173
/// Requirements:
174174
///
175-
/// The function must have a body.
175+
/// The function must have a body, i.e.
176+
/// `FunctionDecl::doesThisDecalarationHaveABody()` must be true.
176177
void initialize();
177178

178179
/// Returns a new environment that is a copy of this one.

clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ static llvm::BitVector findReachableBlocks(const CFG &Cfg) {
6969

7070
llvm::Expected<ControlFlowContext>
7171
ControlFlowContext::build(const FunctionDecl &Func) {
72-
if (!Func.hasBody())
72+
if (!Func.doesThisDeclarationHaveABody())
7373
return llvm::createStringError(
7474
std::make_error_code(std::errc::invalid_argument),
7575
"Cannot analyze function without a body");

clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ DataflowAnalysisContext::getControlFlowContext(const FunctionDecl *F) {
298298
if (It != FunctionContexts.end())
299299
return &It->second;
300300

301-
if (F->hasBody()) {
301+
if (F->doesThisDeclarationHaveABody()) {
302302
auto CFCtx = ControlFlowContext::build(*F);
303303
// FIXME: Handle errors.
304304
assert(CFCtx);

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ void Environment::initialize() {
386386
return;
387387

388388
if (const auto *FuncDecl = dyn_cast<FunctionDecl>(DeclCtx)) {
389-
assert(FuncDecl->getBody() != nullptr);
389+
assert(FuncDecl->doesThisDeclarationHaveABody());
390390

391391
initFieldsGlobalsAndFuncs(FuncDecl);
392392

@@ -426,7 +426,7 @@ void Environment::initialize() {
426426
// FIXME: Add support for resetting globals after function calls to enable
427427
// the implementation of sound analyses.
428428
void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) {
429-
assert(FuncDecl->getBody() != nullptr);
429+
assert(FuncDecl->doesThisDeclarationHaveABody());
430430

431431
FieldSet Fields;
432432
llvm::DenseSet<const VarDecl *> Vars;

0 commit comments

Comments
 (0)