Skip to content

Commit

Permalink
[analyzer] Allow padding checker to traverse simple class hierarchies
Browse files Browse the repository at this point in the history
The existing padding checker skips classes that have any base classes. 
This patch allows the checker to traverse very simple cases: 
classes that have no fields and have exactly one base class. 
This is important mostly in the case of array declarations.

Patch by Max Bernstein!

Test plan: make check-all

Differential revision: https://reviews.llvm.org/D53206

llvm-svn: 345558
  • Loading branch information
alexander-shaposhnikov committed Oct 30, 2018
1 parent 7c180fa commit e2f0734
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 8 deletions.
38 changes: 30 additions & 8 deletions clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
Expand Up @@ -75,6 +75,20 @@ class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
if (shouldSkipDecl(RD))
return;

// TODO: Figure out why we are going through declarations and not only
// definitions.
if (!(RD = RD->getDefinition()))
return;

// This is the simplest correct case: a class with no fields and one base
// class. Other cases are more complicated because of how the base classes
// & fields might interact, so we don't bother dealing with them.
// TODO: Support other combinations of base classes and fields.
if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
if (CXXRD->field_empty() && CXXRD->getNumBases() == 1)
return visitRecord(CXXRD->bases().begin()->getType()->getAsRecordDecl(),
PadMultiplier);

auto &ASTContext = RD->getASTContext();
const ASTRecordLayout &RL = ASTContext.getASTRecordLayout(RD);
assert(llvm::isPowerOf2_64(RL.getAlignment().getQuantity()));
Expand Down Expand Up @@ -112,12 +126,15 @@ class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
if (RT == nullptr)
return;

// TODO: Recurse into the fields and base classes to see if any
// of those have excess padding.
// TODO: Recurse into the fields to see if they have excess padding.
visitRecord(RT->getDecl(), Elts);
}

bool shouldSkipDecl(const RecordDecl *RD) const {
// TODO: Figure out why we are going through declarations and not only
// definitions.
if (!(RD = RD->getDefinition()))
return true;
auto Location = RD->getLocation();
// If the construct doesn't have a source file, then it's not something
// we want to diagnose.
Expand All @@ -132,13 +149,14 @@ class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
// Not going to attempt to optimize unions.
if (RD->isUnion())
return true;
// How do you reorder fields if you haven't got any?
if (RD->field_empty())
return true;
if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
// Tail padding with base classes ends up being very complicated.
// We will skip objects with base classes for now.
if (CXXRD->getNumBases() != 0)
// We will skip objects with base classes for now, unless they do not
// have fields.
// TODO: Handle more base class scenarios.
if (!CXXRD->field_empty() && CXXRD->getNumBases() != 0)
return true;
if (CXXRD->field_empty() && CXXRD->getNumBases() != 1)
return true;
// Virtual bases are complicated, skipping those for now.
if (CXXRD->getNumVBases() != 0)
Expand All @@ -150,6 +168,10 @@ class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
if (CXXRD->getTypeForDecl()->isInstantiationDependentType())
return true;
}
// How do you reorder fields if you haven't got any?
else if (RD->field_empty())
return true;

auto IsTrickyField = [](const FieldDecl *FD) -> bool {
// Bitfield layout is hard.
if (FD->isBitField())
Expand Down Expand Up @@ -323,7 +345,7 @@ class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
BR->emitReport(std::move(Report));
}
};
}
} // namespace

void ento::registerPaddingChecker(CheckerManager &Mgr) {
Mgr.registerChecker<PaddingChecker>();
Expand Down
28 changes: 28 additions & 0 deletions clang/test/Analysis/padding_inherit.cpp
@@ -0,0 +1,28 @@
// RUN: %clang_analyze_cc1 -std=c++14 -analyzer-checker=optin.performance -analyzer-config optin.performance.Padding:AllowedPad=20 -verify %s

// A class that has no fields and one base class should visit that base class
// instead. Note that despite having excess padding of 2, this is flagged
// because of its usage in an array of 100 elements below (`ais').
// TODO: Add a note to the bug report with BugReport::addNote() to mention the
// variable using the class and also mention what class is inherting from what.
// expected-warning@+1{{Excessive padding in 'struct FakeIntSandwich'}}
struct FakeIntSandwich {
char c1;
int i;
char c2;
};

struct AnotherIntSandwich : FakeIntSandwich { // no-warning
};

// But we don't yet support multiple base classes.
struct IntSandwich {};
struct TooManyBaseClasses : FakeIntSandwich, IntSandwich { // no-warning
};

AnotherIntSandwich ais[100];

struct Empty {};
struct DoubleEmpty : Empty { // no-warning
Empty e;
};

0 comments on commit e2f0734

Please sign in to comment.