forked from OSchip/llvm-project
[analyzer][CrossTU] Extend CTU to VarDecls with initializer
Summary: The existing CTU mechanism imports `FunctionDecl`s where the definition is available in another TU. This patch extends that to VarDecls, to bind more constants. - Add VarDecl importing functionality to CrossTranslationUnitContext - Import Decls while traversing them in AnalysisConsumer - Add VarDecls to CTU external mappings generator - Name changes from "external function map" to "external definition map" Reviewers: NoQ, dcoughlin, xazax.hun, george.karpenkov, martong Reviewed By: xazax.hun Subscribers: Charusso, baloghadamsoftware, mikhail.ramalho, Szelethus, donat.nagy, dkrupp, george.karpenkov, mgorny, whisperity, szepet, rnkovacs, a.sidorin, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D46421 llvm-svn: 358968
This commit is contained in:
parent
330bc8af13
commit
850361f6c1
|
@ -28,6 +28,7 @@ class ASTImporter;
|
|||
class ASTUnit;
|
||||
class DeclContext;
|
||||
class FunctionDecl;
|
||||
class VarDecl;
|
||||
class NamedDecl;
|
||||
class TranslationUnitDecl;
|
||||
|
||||
|
@ -87,6 +88,9 @@ parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir);
|
|||
|
||||
std::string createCrossTUIndexString(const llvm::StringMap<std::string> &Index);
|
||||
|
||||
// Returns true if the variable or any field of a record variable is const.
|
||||
bool containsConst(const VarDecl *VD, const ASTContext &ACtx);
|
||||
|
||||
/// This class is used for tools that requires cross translation
|
||||
/// unit capability.
|
||||
///
|
||||
|
@ -102,16 +106,16 @@ public:
|
|||
CrossTranslationUnitContext(CompilerInstance &CI);
|
||||
~CrossTranslationUnitContext();
|
||||
|
||||
/// This function loads a function definition from an external AST
|
||||
/// file and merge it into the original AST.
|
||||
/// This function loads a function or variable definition from an
|
||||
/// external AST file and merges it into the original AST.
|
||||
///
|
||||
/// This method should only be used on functions that have no definitions in
|
||||
/// This method should only be used on functions that have no definitions or
|
||||
/// variables that have no initializer in
|
||||
/// the current translation unit. A function definition with the same
|
||||
/// declaration will be looked up in the index file which should be in the
|
||||
/// \p CrossTUDir directory, called \p IndexName. In case the declaration is
|
||||
/// found in the index the corresponding AST file will be loaded and the
|
||||
/// definition of the function will be merged into the original AST using
|
||||
/// the AST Importer.
|
||||
/// definition will be merged into the original AST using the AST Importer.
|
||||
///
|
||||
/// \return The declaration with the definition will be returned.
|
||||
/// If no suitable definition is found in the index file or multiple
|
||||
|
@ -121,17 +125,19 @@ public:
|
|||
llvm::Expected<const FunctionDecl *>
|
||||
getCrossTUDefinition(const FunctionDecl *FD, StringRef CrossTUDir,
|
||||
StringRef IndexName, bool DisplayCTUProgress = false);
|
||||
llvm::Expected<const VarDecl *>
|
||||
getCrossTUDefinition(const VarDecl *VD, StringRef CrossTUDir,
|
||||
StringRef IndexName, bool DisplayCTUProgress = false);
|
||||
|
||||
/// This function loads a function definition from an external AST
|
||||
/// file.
|
||||
/// This function loads a definition from an external AST file.
|
||||
///
|
||||
/// A function definition with the same declaration will be looked up in the
|
||||
/// A definition with the same declaration will be looked up in the
|
||||
/// index file which should be in the \p CrossTUDir directory, called
|
||||
/// \p IndexName. In case the declaration is found in the index the
|
||||
/// corresponding AST file will be loaded.
|
||||
///
|
||||
/// \return Returns a pointer to the ASTUnit that contains the definition of
|
||||
/// the looked up function or an Error.
|
||||
/// the looked up name or an Error.
|
||||
/// The returned pointer is never a nullptr.
|
||||
///
|
||||
/// Note that the AST files should also be in the \p CrossTUDir.
|
||||
|
@ -146,8 +152,9 @@ public:
|
|||
///
|
||||
/// \return Returns the resulting definition or an error.
|
||||
llvm::Expected<const FunctionDecl *> importDefinition(const FunctionDecl *FD);
|
||||
llvm::Expected<const VarDecl *> importDefinition(const VarDecl *VD);
|
||||
|
||||
/// Get a name to identify a function.
|
||||
/// Get a name to identify a named decl.
|
||||
static std::string getLookupName(const NamedDecl *ND);
|
||||
|
||||
/// Emit diagnostics for the user for potential configuration errors.
|
||||
|
@ -156,12 +163,20 @@ public:
|
|||
private:
|
||||
void lazyInitLookupTable(TranslationUnitDecl *ToTU);
|
||||
ASTImporter &getOrCreateASTImporter(ASTContext &From);
|
||||
const FunctionDecl *findFunctionInDeclContext(const DeclContext *DC,
|
||||
StringRef LookupFnName);
|
||||
template <typename T>
|
||||
llvm::Expected<const T *> getCrossTUDefinitionImpl(const T *D,
|
||||
StringRef CrossTUDir,
|
||||
StringRef IndexName,
|
||||
bool DisplayCTUProgress);
|
||||
template <typename T>
|
||||
const T *findDefInDeclContext(const DeclContext *DC,
|
||||
StringRef LookupName);
|
||||
template <typename T>
|
||||
llvm::Expected<const T *> importDefinitionImpl(const T *D);
|
||||
|
||||
llvm::StringMap<std::unique_ptr<clang::ASTUnit>> FileASTUnitMap;
|
||||
llvm::StringMap<clang::ASTUnit *> FunctionASTUnitMap;
|
||||
llvm::StringMap<std::string> FunctionFileMap;
|
||||
llvm::StringMap<clang::ASTUnit *> NameASTUnitMap;
|
||||
llvm::StringMap<std::string> NameFileMap;
|
||||
llvm::DenseMap<TranslationUnitDecl *, std::unique_ptr<ASTImporter>>
|
||||
ASTUnitImporterMap;
|
||||
CompilerInstance &CI;
|
||||
|
|
|
@ -158,6 +158,27 @@ createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {
|
|||
return Result.str();
|
||||
}
|
||||
|
||||
bool containsConst(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;
|
||||
}
|
||||
|
||||
static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD) {
|
||||
return D->hasBody(DefD);
|
||||
}
|
||||
static bool hasBodyOrInit(const VarDecl *D, const VarDecl *&DefD) {
|
||||
return D->getAnyInitializer(DefD);
|
||||
}
|
||||
template <typename T> static bool hasBodyOrInit(const T *D) {
|
||||
const T *Unused;
|
||||
return hasBodyOrInit(D, Unused);
|
||||
}
|
||||
|
||||
CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)
|
||||
: CI(CI), Context(CI.getASTContext()) {}
|
||||
|
||||
|
@ -165,48 +186,50 @@ CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
|
|||
|
||||
std::string CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) {
|
||||
SmallString<128> DeclUSR;
|
||||
bool Ret = index::generateUSRForDecl(ND, DeclUSR); (void)Ret;
|
||||
bool Ret = index::generateUSRForDecl(ND, DeclUSR);
|
||||
(void)Ret;
|
||||
assert(!Ret && "Unable to generate USR");
|
||||
return DeclUSR.str();
|
||||
}
|
||||
|
||||
/// Recursively visits the function decls of a DeclContext, and looks up a
|
||||
/// function based on USRs.
|
||||
const FunctionDecl *
|
||||
CrossTranslationUnitContext::findFunctionInDeclContext(const DeclContext *DC,
|
||||
StringRef LookupFnName) {
|
||||
/// Recursively visits the decls of a DeclContext, and returns one with the
|
||||
/// given USR.
|
||||
template <typename T>
|
||||
const T *
|
||||
CrossTranslationUnitContext::findDefInDeclContext(const DeclContext *DC,
|
||||
StringRef LookupName) {
|
||||
assert(DC && "Declaration Context must not be null");
|
||||
for (const Decl *D : DC->decls()) {
|
||||
const auto *SubDC = dyn_cast<DeclContext>(D);
|
||||
if (SubDC)
|
||||
if (const auto *FD = findFunctionInDeclContext(SubDC, LookupFnName))
|
||||
return FD;
|
||||
if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))
|
||||
return ND;
|
||||
|
||||
const auto *ND = dyn_cast<FunctionDecl>(D);
|
||||
const FunctionDecl *ResultDecl;
|
||||
if (!ND || !ND->hasBody(ResultDecl))
|
||||
const auto *ND = dyn_cast<T>(D);
|
||||
const T *ResultDecl;
|
||||
if (!ND || !hasBodyOrInit(ND, ResultDecl))
|
||||
continue;
|
||||
if (getLookupName(ResultDecl) != LookupFnName)
|
||||
if (getLookupName(ResultDecl) != LookupName)
|
||||
continue;
|
||||
return ResultDecl;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
llvm::Expected<const FunctionDecl *>
|
||||
CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
|
||||
StringRef CrossTUDir,
|
||||
StringRef IndexName,
|
||||
bool DisplayCTUProgress) {
|
||||
assert(FD && "FD is missing, bad call to this function!");
|
||||
assert(!FD->hasBody() && "FD has a definition in current translation unit!");
|
||||
template <typename T>
|
||||
llvm::Expected<const T *> CrossTranslationUnitContext::getCrossTUDefinitionImpl(
|
||||
const T *D, StringRef CrossTUDir, StringRef IndexName,
|
||||
bool DisplayCTUProgress) {
|
||||
assert(D && "D is missing, bad call to this function!");
|
||||
assert(!hasBodyOrInit(D) &&
|
||||
"D has a body or init in current translation unit!");
|
||||
++NumGetCTUCalled;
|
||||
const std::string LookupFnName = getLookupName(FD);
|
||||
if (LookupFnName.empty())
|
||||
const std::string LookupName = getLookupName(D);
|
||||
if (LookupName.empty())
|
||||
return llvm::make_error<IndexError>(
|
||||
index_error_code::failed_to_generate_usr);
|
||||
llvm::Expected<ASTUnit *> ASTUnitOrError =
|
||||
loadExternalAST(LookupFnName, CrossTUDir, IndexName, DisplayCTUProgress);
|
||||
loadExternalAST(LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
|
||||
if (!ASTUnitOrError)
|
||||
return ASTUnitOrError.takeError();
|
||||
ASTUnit *Unit = *ASTUnitOrError;
|
||||
|
@ -262,12 +285,29 @@ CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
|
|||
}
|
||||
|
||||
TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
|
||||
if (const FunctionDecl *ResultDecl =
|
||||
findFunctionInDeclContext(TU, LookupFnName))
|
||||
if (const T *ResultDecl = findDefInDeclContext<T>(TU, LookupName))
|
||||
return importDefinition(ResultDecl);
|
||||
return llvm::make_error<IndexError>(index_error_code::failed_import);
|
||||
}
|
||||
|
||||
llvm::Expected<const FunctionDecl *>
|
||||
CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
|
||||
StringRef CrossTUDir,
|
||||
StringRef IndexName,
|
||||
bool DisplayCTUProgress) {
|
||||
return getCrossTUDefinitionImpl(FD, CrossTUDir, IndexName,
|
||||
DisplayCTUProgress);
|
||||
}
|
||||
|
||||
llvm::Expected<const VarDecl *>
|
||||
CrossTranslationUnitContext::getCrossTUDefinition(const VarDecl *VD,
|
||||
StringRef CrossTUDir,
|
||||
StringRef IndexName,
|
||||
bool DisplayCTUProgress) {
|
||||
return getCrossTUDefinitionImpl(VD, CrossTUDir, IndexName,
|
||||
DisplayCTUProgress);
|
||||
}
|
||||
|
||||
void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {
|
||||
switch (IE.getCode()) {
|
||||
case index_error_code::missing_index_file:
|
||||
|
@ -294,14 +334,14 @@ void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {
|
|||
llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
|
||||
StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,
|
||||
bool DisplayCTUProgress) {
|
||||
// FIXME: The current implementation only supports loading functions with
|
||||
// FIXME: The current implementation only supports loading decls with
|
||||
// a lookup name from a single translation unit. If multiple
|
||||
// translation units contains functions with the same lookup name an
|
||||
// translation units contains decls with the same lookup name an
|
||||
// error will be returned.
|
||||
ASTUnit *Unit = nullptr;
|
||||
auto FnUnitCacheEntry = FunctionASTUnitMap.find(LookupName);
|
||||
if (FnUnitCacheEntry == FunctionASTUnitMap.end()) {
|
||||
if (FunctionFileMap.empty()) {
|
||||
auto NameUnitCacheEntry = NameASTUnitMap.find(LookupName);
|
||||
if (NameUnitCacheEntry == NameASTUnitMap.end()) {
|
||||
if (NameFileMap.empty()) {
|
||||
SmallString<256> IndexFile = CrossTUDir;
|
||||
if (llvm::sys::path::is_absolute(IndexName))
|
||||
IndexFile = IndexName;
|
||||
|
@ -310,13 +350,13 @@ llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
|
|||
llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
|
||||
parseCrossTUIndex(IndexFile, CrossTUDir);
|
||||
if (IndexOrErr)
|
||||
FunctionFileMap = *IndexOrErr;
|
||||
NameFileMap = *IndexOrErr;
|
||||
else
|
||||
return IndexOrErr.takeError();
|
||||
}
|
||||
|
||||
auto It = FunctionFileMap.find(LookupName);
|
||||
if (It == FunctionFileMap.end()) {
|
||||
auto It = NameFileMap.find(LookupName);
|
||||
if (It == NameFileMap.end()) {
|
||||
++NumNotInOtherTU;
|
||||
return llvm::make_error<IndexError>(index_error_code::missing_definition);
|
||||
}
|
||||
|
@ -342,9 +382,9 @@ llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
|
|||
} else {
|
||||
Unit = ASTCacheEntry->second.get();
|
||||
}
|
||||
FunctionASTUnitMap[LookupName] = Unit;
|
||||
NameASTUnitMap[LookupName] = Unit;
|
||||
} else {
|
||||
Unit = FnUnitCacheEntry->second;
|
||||
Unit = NameUnitCacheEntry->second;
|
||||
}
|
||||
if (!Unit)
|
||||
return llvm::make_error<IndexError>(
|
||||
|
@ -352,12 +392,13 @@ llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
|
|||
return Unit;
|
||||
}
|
||||
|
||||
llvm::Expected<const FunctionDecl *>
|
||||
CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) {
|
||||
assert(FD->hasBody() && "Functions to be imported should have body.");
|
||||
template <typename T>
|
||||
llvm::Expected<const T *>
|
||||
CrossTranslationUnitContext::importDefinitionImpl(const T *D) {
|
||||
assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");
|
||||
|
||||
ASTImporter &Importer = getOrCreateASTImporter(FD->getASTContext());
|
||||
auto ToDeclOrError = Importer.Import_New(FD);
|
||||
ASTImporter &Importer = getOrCreateASTImporter(D->getASTContext());
|
||||
auto ToDeclOrError = Importer.Import_New(D);
|
||||
if (!ToDeclOrError) {
|
||||
handleAllErrors(ToDeclOrError.takeError(),
|
||||
[&](const ImportError &IE) {
|
||||
|
@ -375,13 +416,23 @@ CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) {
|
|||
});
|
||||
return llvm::make_error<IndexError>(index_error_code::failed_import);
|
||||
}
|
||||
auto *ToDecl = cast<FunctionDecl>(*ToDeclOrError);
|
||||
assert(ToDecl->hasBody() && "Imported function should have body.");
|
||||
auto *ToDecl = cast<T>(*ToDeclOrError);
|
||||
assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");
|
||||
++NumGetCTUSuccess;
|
||||
|
||||
return ToDecl;
|
||||
}
|
||||
|
||||
llvm::Expected<const FunctionDecl *>
|
||||
CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) {
|
||||
return importDefinitionImpl(FD);
|
||||
}
|
||||
|
||||
llvm::Expected<const VarDecl *>
|
||||
CrossTranslationUnitContext::importDefinition(const VarDecl *VD) {
|
||||
return importDefinitionImpl(VD);
|
||||
}
|
||||
|
||||
void CrossTranslationUnitContext::lazyInitLookupTable(
|
||||
TranslationUnitDecl *ToTU) {
|
||||
if (!LookupTable)
|
||||
|
|
|
@ -1655,7 +1655,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
|
|||
const VarDecl *VD = VR->getDecl();
|
||||
// Either the array or the array element has to be const.
|
||||
if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) {
|
||||
if (const Expr *Init = VD->getInit()) {
|
||||
if (const Expr *Init = VD->getAnyInitializer()) {
|
||||
if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
|
||||
// The array index has to be known.
|
||||
if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
|
||||
|
@ -1745,7 +1745,7 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
|
|||
unsigned Index = FD->getFieldIndex();
|
||||
// Either the record variable or the field has to be const qualified.
|
||||
if (RecordVarTy.isConstQualified() || Ty.isConstQualified())
|
||||
if (const Expr *Init = VD->getInit())
|
||||
if (const Expr *Init = VD->getAnyInitializer())
|
||||
if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
|
||||
if (Index < InitList->getNumInits()) {
|
||||
if (const Expr *FieldInit = InitList->getInit(Index))
|
||||
|
@ -1943,7 +1943,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
|
|||
|
||||
// Is 'VD' declared constant? If so, retrieve the constant value.
|
||||
if (VD->getType().isConstQualified()) {
|
||||
if (const Expr *Init = VD->getInit()) {
|
||||
if (const Expr *Init = VD->getAnyInitializer()) {
|
||||
if (Optional<SVal> V = svalBuilder.getConstantVal(Init))
|
||||
return *V;
|
||||
|
||||
|
|
|
@ -342,6 +342,35 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool VisitVarDecl(VarDecl *VD) {
|
||||
if (!Opts->IsNaiveCTUEnabled)
|
||||
return true;
|
||||
|
||||
if (VD->hasExternalStorage() || VD->isStaticDataMember()) {
|
||||
if (!cross_tu::containsConst(VD, *Ctx))
|
||||
return true;
|
||||
} else {
|
||||
// Cannot be initialized in another TU.
|
||||
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"))
|
||||
|
|
|
@ -80,3 +80,41 @@ int other_macro_diag(int x) {
|
|||
MACRODIAG();
|
||||
return x;
|
||||
}
|
||||
|
||||
extern const int extInt = 2;
|
||||
namespace intns {
|
||||
extern const int extInt = 3;
|
||||
}
|
||||
struct S {
|
||||
int a;
|
||||
};
|
||||
extern const S extS = {.a = 4};
|
||||
struct A {
|
||||
static const int a;
|
||||
};
|
||||
const int A::a = 3;
|
||||
struct SC {
|
||||
const int a;
|
||||
};
|
||||
SC extSC = {.a = 8};
|
||||
struct ST {
|
||||
static struct SC sc;
|
||||
};
|
||||
struct SC ST::sc = {.a = 2};
|
||||
struct SCNest {
|
||||
struct SCN {
|
||||
const int a;
|
||||
} scn;
|
||||
};
|
||||
SCNest extSCN = {.scn = {.a = 9}};
|
||||
SCNest::SCN extSubSCN = {.a = 1};
|
||||
struct SCC {
|
||||
SCC(int c) : a(c) {}
|
||||
const int a;
|
||||
};
|
||||
SCC extSCC{7};
|
||||
union U {
|
||||
const int a;
|
||||
const unsigned int b;
|
||||
};
|
||||
U extU = {.a = 4};
|
||||
|
|
|
@ -13,3 +13,13 @@ c:@N@chns@S@chcls@F@chf4#I# ctu-chain.cpp.ast
|
|||
c:@N@chns@F@chf2#I# ctu-chain.cpp.ast
|
||||
c:@F@fun_using_anon_struct#I# ctu-other.cpp.ast
|
||||
c:@F@other_macro_diag#I# ctu-other.cpp.ast
|
||||
c:@extInt ctu-other.cpp.ast
|
||||
c:@N@intns@extInt ctu-other.cpp.ast
|
||||
c:@extS ctu-other.cpp.ast
|
||||
c:@S@A@a ctu-other.cpp.ast
|
||||
c:@extSC ctu-other.cpp.ast
|
||||
c:@S@ST@sc ctu-other.cpp.ast
|
||||
c:@extSCN ctu-other.cpp.ast
|
||||
c:@extSubSCN ctu-other.cpp.ast
|
||||
c:@extSCC ctu-other.cpp.ast
|
||||
c:@extU ctu-other.cpp.ast
|
||||
|
|
|
@ -60,6 +60,44 @@ int chf1(int x);
|
|||
int fun_using_anon_struct(int);
|
||||
int other_macro_diag(int);
|
||||
|
||||
extern const int extInt;
|
||||
namespace intns {
|
||||
extern const int extInt;
|
||||
}
|
||||
struct S {
|
||||
int a;
|
||||
};
|
||||
extern const S extS;
|
||||
extern const int extHere;
|
||||
const int extHere = 6;
|
||||
struct A {
|
||||
static const int a;
|
||||
};
|
||||
struct SC {
|
||||
const int a;
|
||||
};
|
||||
extern SC extSC;
|
||||
struct ST {
|
||||
static struct SC sc;
|
||||
};
|
||||
struct SCNest {
|
||||
struct SCN {
|
||||
const int a;
|
||||
} scn;
|
||||
};
|
||||
extern SCNest extSCN;
|
||||
extern SCNest::SCN extSubSCN;
|
||||
struct SCC {
|
||||
SCC(int c);
|
||||
const int a;
|
||||
};
|
||||
extern SCC extSCC;
|
||||
union U {
|
||||
const int a;
|
||||
const unsigned int b;
|
||||
};
|
||||
extern U extU;
|
||||
|
||||
int main() {
|
||||
clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}}
|
||||
|
@ -80,4 +118,16 @@ int main() {
|
|||
clang_analyzer_eval(other_macro_diag(1) == 1); // expected-warning{{TRUE}}
|
||||
// expected-warning@Inputs/ctu-other.cpp:80{{REACHABLE}}
|
||||
MACRODIAG(); // expected-warning{{REACHABLE}}
|
||||
|
||||
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(extHere == 6); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(A::a == 3); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(extSC.a == 8); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(ST::sc.a == 2); // expected-warning{{TRUE}}
|
||||
// clang_analyzer_eval(extSCN.scn.a == 9); // TODO
|
||||
clang_analyzer_eval(extSubSCN.a == 1); // expected-warning{{TRUE}}
|
||||
// clang_analyzer_eval(extSCC.a == 7); // TODO
|
||||
clang_analyzer_eval(extU.a == 4); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,43 @@
|
|||
// RUN: %clang_extdef_map %s -- | FileCheck %s
|
||||
// RUN: %clang_extdef_map %s -- | FileCheck --implicit-check-not "c:@y" --implicit-check-not "c:@z" %s
|
||||
|
||||
int f(int) {
|
||||
return 0;
|
||||
}
|
||||
// CHECK-DAG: c:@F@f#I#
|
||||
|
||||
// CHECK: c:@F@f#I#
|
||||
extern const int x = 5;
|
||||
// CHECK-DAG: c:@x
|
||||
|
||||
// Non-const variables should not be collected.
|
||||
int y = 5;
|
||||
|
||||
// In C++, const implies internal linkage, so not collected.
|
||||
const int z = 5;
|
||||
|
||||
struct S {
|
||||
int a;
|
||||
};
|
||||
extern S const s = {.a = 2};
|
||||
// CHECK-DAG: c:@s
|
||||
|
||||
struct SF {
|
||||
const int a;
|
||||
};
|
||||
SF sf = {.a = 2};
|
||||
// CHECK-DAG: c:@sf
|
||||
|
||||
struct SStatic {
|
||||
static const int a = 4;
|
||||
};
|
||||
const int SStatic::a;
|
||||
// CHECK-DAG: c:@S@SStatic@a
|
||||
|
||||
extern int const arr[5] = { 0, 1 };
|
||||
// CHECK-DAG: c:@arr
|
||||
|
||||
union U {
|
||||
const int a;
|
||||
const unsigned int b;
|
||||
};
|
||||
U u = {.a = 6};
|
||||
// CHECK-DAG: c:@u
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
|
||||
// XFAIL: *
|
||||
|
||||
void clang_analyzer_eval(int);
|
||||
|
||||
extern const int extInt;
|
||||
|
||||
int main()
|
||||
{
|
||||
clang_analyzer_eval(extInt == 2); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
extern const int extInt = 2;
|
|
@ -34,20 +34,22 @@ static cl::OptionCategory ClangExtDefMapGenCategory("clang-extdefmapgen options"
|
|||
class MapExtDefNamesConsumer : public ASTConsumer {
|
||||
public:
|
||||
MapExtDefNamesConsumer(ASTContext &Context)
|
||||
: SM(Context.getSourceManager()) {}
|
||||
: Ctx(Context), SM(Context.getSourceManager()) {}
|
||||
|
||||
~MapExtDefNamesConsumer() {
|
||||
// Flush results to standard output.
|
||||
llvm::outs() << createCrossTUIndexString(Index);
|
||||
}
|
||||
|
||||
void HandleTranslationUnit(ASTContext &Ctx) override {
|
||||
handleDecl(Ctx.getTranslationUnitDecl());
|
||||
void HandleTranslationUnit(ASTContext &Context) override {
|
||||
handleDecl(Context.getTranslationUnitDecl());
|
||||
}
|
||||
|
||||
private:
|
||||
void handleDecl(const Decl *D);
|
||||
void addIfInMain(const DeclaratorDecl *DD, SourceLocation defStart);
|
||||
|
||||
ASTContext &Ctx;
|
||||
SourceManager &SM;
|
||||
llvm::StringMap<std::string> Index;
|
||||
std::string CurrentFileName;
|
||||
|
@ -58,30 +60,13 @@ void MapExtDefNamesConsumer::handleDecl(const Decl *D) {
|
|||
return;
|
||||
|
||||
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
|
||||
if (FD->isThisDeclarationADefinition()) {
|
||||
if (const Stmt *Body = FD->getBody()) {
|
||||
if (CurrentFileName.empty()) {
|
||||
CurrentFileName =
|
||||
SM.getFileEntryForID(SM.getMainFileID())->tryGetRealPathName();
|
||||
if (CurrentFileName.empty())
|
||||
CurrentFileName = "invalid_file";
|
||||
}
|
||||
|
||||
switch (FD->getLinkageInternal()) {
|
||||
case ExternalLinkage:
|
||||
case VisibleNoLinkage:
|
||||
case UniqueExternalLinkage:
|
||||
if (SM.isInMainFile(Body->getBeginLoc())) {
|
||||
std::string LookupName =
|
||||
CrossTranslationUnitContext::getLookupName(FD);
|
||||
Index[LookupName] = CurrentFileName;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FD->isThisDeclarationADefinition())
|
||||
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 (const Expr *Init = VD->getInit())
|
||||
addIfInMain(VD, Init->getBeginLoc());
|
||||
}
|
||||
|
||||
if (const auto *DC = dyn_cast<DeclContext>(D))
|
||||
|
@ -89,6 +74,27 @@ void MapExtDefNamesConsumer::handleDecl(const Decl *D) {
|
|||
handleDecl(D);
|
||||
}
|
||||
|
||||
void MapExtDefNamesConsumer::addIfInMain(const DeclaratorDecl *DD,
|
||||
SourceLocation defStart) {
|
||||
std::string LookupName = CrossTranslationUnitContext::getLookupName(DD);
|
||||
if (CurrentFileName.empty()) {
|
||||
CurrentFileName =
|
||||
SM.getFileEntryForID(SM.getMainFileID())->tryGetRealPathName();
|
||||
if (CurrentFileName.empty())
|
||||
CurrentFileName = "invalid_file";
|
||||
}
|
||||
|
||||
switch (DD->getLinkageInternal()) {
|
||||
case ExternalLinkage:
|
||||
case VisibleNoLinkage:
|
||||
case UniqueExternalLinkage:
|
||||
if (SM.isInMainFile(defStart))
|
||||
Index[LookupName] = CurrentFileName;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class MapExtDefNamesAction : public ASTFrontendAction {
|
||||
protected:
|
||||
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
|
||||
|
|
Loading…
Reference in New Issue