[analyzer][ctu] Only import const and trivial VarDecls

Do import the definition of objects from a foreign translation unit if that's type is const and trivial.

Differential Revision: https://reviews.llvm.org/D122805
This commit is contained in:
Gabor Marton 2022-04-01 11:58:17 +02:00
parent 111cb395c9
commit e63b81d10e
8 changed files with 36 additions and 22 deletions

View File

@ -109,8 +109,10 @@ llvm::Expected<InvocationListTy> parseInvocationList(
StringRef FileContent,
llvm::sys::path::Style PathStyle = llvm::sys::path::Style::posix);
// Returns true if the variable or any field of a record variable is const.
bool containsConst(const VarDecl *VD, const ASTContext &ACtx);
/// Returns true if it makes sense to import a foreign variable definition.
/// For instance, we don't want to import variables that have non-trivial types
/// because the constructor might have side-effects.
bool shouldImport(const VarDecl *VD, const ASTContext &ACtx);
/// This class is used for tools that requires cross translation
/// unit capability.

View File

@ -220,14 +220,9 @@ createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {
return Result.str();
}
bool containsConst(const VarDecl *VD, const ASTContext &ACtx) {
bool shouldImport(const VarDecl *VD, const ASTContext &ACtx) {
CanQualType CT = ACtx.getCanonicalType(VD->getType());
if (!CT.isConstQualified()) {
const RecordType *RTy = CT->getAs<RecordType>();
if (!RTy || !RTy->hasConstFields())
return false;
}
return true;
return CT.isConstQualified() && VD->getType().isTrivialType(ACtx);
}
static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD) {

View File

@ -289,7 +289,7 @@ public:
return true;
if (VD->hasExternalStorage() || VD->isStaticDataMember()) {
if (!cross_tu::containsConst(VD, *Ctx))
if (!cross_tu::shouldImport(VD, *Ctx))
return true;
} else {
// Cannot be initialized in another TU.

View File

@ -102,6 +102,12 @@ struct S {
int a;
};
extern const S extS = {.a = 4};
extern S extNonConstS = {.a = 4};
struct NonTrivialS {
int a;
~NonTrivialS();
};
extern const NonTrivialS extNTS = {.a = 4};
struct A {
static const int a;
};
@ -109,18 +115,18 @@ const int A::a = 3;
struct SC {
const int a;
};
SC extSC = {.a = 8};
extern const SC extSC = {.a = 8};
struct ST {
static struct SC sc;
static const struct SC sc;
};
struct SC ST::sc = {.a = 2};
const struct SC ST::sc = {.a = 2};
struct SCNest {
struct SCN {
const int a;
} scn;
};
SCNest extSCN = {.scn = {.a = 9}};
SCNest::SCN extSubSCN = {.a = 1};
extern SCNest::SCN const extSubSCN = {.a = 1};
struct SCC {
SCC(int c) : a(c) {}
const int a;
@ -130,7 +136,7 @@ union U {
const int a;
const unsigned int b;
};
U extU = {.a = 4};
extern const U extU = {.a = 4};
class TestAnonUnionUSR {
public:

View File

@ -18,6 +18,7 @@
9:c:@extInt ctu-other.cpp.ast
17:c:@N@intns@extInt ctu-other.cpp.ast
7:c:@extS ctu-other.cpp.ast
9:c:@extNTS ctu-other.cpp.ast
8:c:@S@A@a ctu-other.cpp.ast
8:c:@extSC ctu-other.cpp.ast
10:c:@S@ST@sc ctu-other.cpp.ast

View File

@ -74,6 +74,13 @@ struct S {
int a;
};
extern const S extS;
extern S extNonConstS;
struct NonTrivialS {
int a;
// User declaring a dtor makes it non-trivial.
~NonTrivialS();
};
extern const NonTrivialS extNTS;
extern const int extHere;
const int extHere = 6;
struct A {
@ -82,9 +89,9 @@ struct A {
struct SC {
const int a;
};
extern SC extSC;
extern const SC extSC;
struct ST {
static struct SC sc;
static const struct SC sc;
};
struct SCNest {
struct SCN {
@ -92,7 +99,7 @@ struct SCNest {
} scn;
};
extern SCNest extSCN;
extern SCNest::SCN extSubSCN;
extern const SCNest::SCN extSubSCN;
struct SCC {
SCC(int c);
const int a;
@ -102,7 +109,7 @@ union U {
const int a;
const unsigned int b;
};
extern U extU;
extern const U extU;
void test_virtual_functions(mycls* obj) {
// The dynamic type is known.
@ -153,6 +160,9 @@ int main() {
clang_analyzer_eval(extInt == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(intns::extInt == 3); // expected-warning{{TRUE}}
clang_analyzer_eval(extS.a == 4); // expected-warning{{TRUE}}
clang_analyzer_eval(extNonConstS.a == 4); // expected-warning{{TRUE}} expected-warning{{FALSE}}
// Do not import non-trivial classes' initializers.
clang_analyzer_eval(extNTS.a == 4); // expected-warning{{TRUE}} expected-warning{{FALSE}}
clang_analyzer_eval(extHere == 6); // expected-warning{{TRUE}}
clang_analyzer_eval(A::a == 3); // expected-warning{{TRUE}}
clang_analyzer_eval(extSC.a == 8); // expected-warning{{TRUE}}

View File

@ -23,7 +23,7 @@ extern S const s = {.a = 2};
struct SF {
const int a;
};
SF sf = {.a = 2};
extern const SF sf = {.a = 2};
// CHECK-DAG: 5:c:@sf
struct SStatic {
@ -39,7 +39,7 @@ union U {
const int a;
const unsigned int b;
};
U u = {.a = 6};
extern const U u = {.a = 6};
// CHECK-DAG: 4:c:@u
// No USR can be generated for this.

View File

@ -64,7 +64,7 @@ void MapExtDefNamesConsumer::handleDecl(const Decl *D) {
if (const Stmt *Body = FD->getBody())
addIfInMain(FD, Body->getBeginLoc());
} else if (const auto *VD = dyn_cast<VarDecl>(D)) {
if (cross_tu::containsConst(VD, Ctx) && VD->hasInit())
if (cross_tu::shouldImport(VD, Ctx) && VD->hasInit())
if (const Expr *Init = VD->getInit())
addIfInMain(VD, Init->getBeginLoc());
}