summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/AST/ASTImporter.h7
-rw-r--r--clang/lib/AST/ASTImporter.cpp44
-rw-r--r--clang/test/Analysis/Inputs/ctu-other.cpp3
-rw-r--r--clang/test/Analysis/Inputs/externalFnMap.txt1
-rw-r--r--clang/test/Analysis/ctu-main.cpp3
-rw-r--r--clang/unittests/AST/ASTImporterTest.cpp296
6 files changed, 282 insertions, 72 deletions
diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h
index 42e9e85b97e..55e46eb2010 100644
--- a/clang/include/clang/AST/ASTImporter.h
+++ b/clang/include/clang/AST/ASTImporter.h
@@ -83,10 +83,6 @@ class TypeSourceInfo;
/// the "from" source manager to the corresponding CXXBasesSpecifier
/// in the "to" source manager.
ImportedCXXBaseSpecifierMap ImportedCXXBaseSpecifiers;
-
- /// \brief Imported, anonymous tag declarations that are missing their
- /// corresponding typedefs.
- SmallVector<TagDecl *, 4> AnonTagsWithPendingTypedefs;
/// \brief Declaration (from, to) pairs that are known not to be equivalent
/// (which we have already complained about).
@@ -136,6 +132,9 @@ class TypeSourceInfo;
/// \returns the equivalent declaration in the "to" context, or a NULL type
/// if an error occurred.
Decl *Import(Decl *FromD);
+ Decl *Import(const Decl *FromD) {
+ return Import(const_cast<Decl *>(FromD));
+ }
/// \brief Return the copy of the given declaration in the "to" context if
/// it has already been imported from the "from" context. Otherwise return
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 9d87157e271..a2e6c01dc29 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1088,6 +1088,17 @@ void ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
Importer.Import(From);
}
+static void setTypedefNameForAnonDecl(TagDecl *From, TagDecl *To,
+ ASTImporter &Importer) {
+ if (TypedefNameDecl *FromTypedef = From->getTypedefNameForAnonDecl()) {
+ auto *ToTypedef =
+ cast_or_null<TypedefNameDecl>(Importer.Import(FromTypedef));
+ assert (ToTypedef && "Failed to import typedef of an anonymous structure");
+
+ To->setTypedefNameForAnonDecl(ToTypedef);
+ }
+}
+
bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To,
ImportDefinitionKind Kind) {
if (To->getDefinition() || To->isBeingDefined()) {
@@ -1098,6 +1109,8 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To,
}
To->startDefinition();
+
+ setTypedefNameForAnonDecl(From, To, Importer);
// Add base classes.
if (auto *ToCXX = dyn_cast<CXXRecordDecl>(To)) {
@@ -1229,6 +1242,8 @@ bool ASTNodeImporter::ImportDefinition(EnumDecl *From, EnumDecl *To,
To->startDefinition();
+ setTypedefNameForAnonDecl(From, To, Importer);
+
QualType T = Importer.Import(Importer.getFromContext().getTypeDeclType(From));
if (T.isNull())
return true;
@@ -1707,6 +1722,11 @@ Decl *ASTNodeImporter::VisitTypedefNameDecl(TypedefNameDecl *D, bool IsAlias) {
if (T.isNull())
return nullptr;
+ // Some nodes (like anonymous tags referred by typedefs) are allowed to
+ // import their enclosing typedef directly. Check if this is the case.
+ if (Decl *AlreadyImported = Importer.GetAlreadyImportedOrNull(D))
+ return AlreadyImported;
+
// Create the new typedef node.
TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo());
SourceLocation StartL = Importer.Import(D->getLocStart());
@@ -6576,29 +6596,7 @@ Decl *ASTImporter::Import(Decl *FromD) {
// Record the imported declaration.
ImportedDecls[FromD] = ToD;
-
- if (auto *FromTag = dyn_cast<TagDecl>(FromD)) {
- // Keep track of anonymous tags that have an associated typedef.
- if (FromTag->getTypedefNameForAnonDecl())
- AnonTagsWithPendingTypedefs.push_back(FromTag);
- } else if (auto *FromTypedef = dyn_cast<TypedefNameDecl>(FromD)) {
- // When we've finished transforming a typedef, see whether it was the
- // typedef for an anonymous tag.
- for (SmallVectorImpl<TagDecl *>::iterator
- FromTag = AnonTagsWithPendingTypedefs.begin(),
- FromTagEnd = AnonTagsWithPendingTypedefs.end();
- FromTag != FromTagEnd; ++FromTag) {
- if ((*FromTag)->getTypedefNameForAnonDecl() == FromTypedef) {
- if (auto *ToTag = cast_or_null<TagDecl>(Import(*FromTag))) {
- // We found the typedef for an anonymous tag; link them.
- ToTag->setTypedefNameForAnonDecl(cast<TypedefNameDecl>(ToD));
- AnonTagsWithPendingTypedefs.erase(FromTag);
- break;
- }
- }
- }
- }
-
+
return ToD;
}
diff --git a/clang/test/Analysis/Inputs/ctu-other.cpp b/clang/test/Analysis/Inputs/ctu-other.cpp
index d35b7b2d12d..eceac5af3c9 100644
--- a/clang/test/Analysis/Inputs/ctu-other.cpp
+++ b/clang/test/Analysis/Inputs/ctu-other.cpp
@@ -65,3 +65,6 @@ int chf1(int x) {
return chf2(x);
}
}
+
+typedef struct { int n; } Anonymous;
+int fun_using_anon_struct(int n) { Anonymous anon; anon.n = n; return anon.n; }
diff --git a/clang/test/Analysis/Inputs/externalFnMap.txt b/clang/test/Analysis/Inputs/externalFnMap.txt
index 2c93d3e869b..805990f25cb 100644
--- a/clang/test/Analysis/Inputs/externalFnMap.txt
+++ b/clang/test/Analysis/Inputs/externalFnMap.txt
@@ -11,3 +11,4 @@ c:@F@h#I# ctu-other.cpp.ast
c:@F@h_chain#I# ctu-chain.cpp.ast
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
diff --git a/clang/test/Analysis/ctu-main.cpp b/clang/test/Analysis/ctu-main.cpp
index cb82cd47d78..3e9db816f26 100644
--- a/clang/test/Analysis/ctu-main.cpp
+++ b/clang/test/Analysis/ctu-main.cpp
@@ -40,6 +40,8 @@ namespace chns {
int chf1(int x);
}
+int fun_using_anon_struct(int);
+
int main() {
clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}}
@@ -55,4 +57,5 @@ int main() {
clang_analyzer_eval(mycls::embed_cls2().fecl2(0) == -11); // expected-warning{{TRUE}}
clang_analyzer_eval(chns::chf1(4) == 12); // expected-warning{{TRUE}}
+ clang_analyzer_eval(fun_using_anon_struct(8) == 8); // expected-warning{{TRUE}}
}
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index ca024b17d3c..9c0d6a3617c 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -20,10 +20,15 @@
#include "DeclMatcher.h"
#include "gtest/gtest.h"
+#include "llvm/ADT/StringMap.h"
namespace clang {
namespace ast_matchers {
+using internal::Matcher;
+using internal::BindableMatcher;
+using llvm::StringMap;
+
typedef std::vector<std::string> ArgVector;
typedef std::vector<ArgVector> RunOptions;
@@ -70,22 +75,61 @@ static RunOptions getRunOptionsForLanguage(Language Lang) {
// Creates a virtual file and assigns that to the context of given AST. If the
// file already exists then the file will not be created again as a duplicate.
-static void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
- const std::string &Code) {
+static void
+createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
+ std::unique_ptr<llvm::MemoryBuffer> &&Buffer) {
assert(ToAST);
ASTContext &ToCtx = ToAST->getASTContext();
auto *OFS = static_cast<vfs::OverlayFileSystem *>(
ToCtx.getSourceManager().getFileManager().getVirtualFileSystem().get());
auto *MFS =
static_cast<vfs::InMemoryFileSystem *>(OFS->overlays_begin()->get());
- MFS->addFile(FileName, 0, llvm::MemoryBuffer::getMemBuffer(Code.c_str()));
+ MFS->addFile(FileName, 0, std::move(Buffer));
+}
+
+static void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
+ StringRef Code) {
+ return createVirtualFileIfNeeded(ToAST, FileName,
+ llvm::MemoryBuffer::getMemBuffer(Code));
+}
+
+template <typename NodeType>
+NodeType importNode(ASTUnit *From, ASTUnit *To, ASTImporter &Importer,
+ NodeType Node) {
+ ASTContext &ToCtx = To->getASTContext();
+
+ // Add 'From' file to virtual file system so importer can 'find' it
+ // while importing SourceLocations. It is safe to add same file multiple
+ // times - it just isn't replaced.
+ StringRef FromFileName = From->getMainFileName();
+ createVirtualFileIfNeeded(To, FromFileName,
+ From->getBufferForFile(FromFileName));
+
+ auto Imported = Importer.Import(Node);
+
+ // This should dump source locations and assert if some source locations
+ // were not imported.
+ SmallString<1024> ImportChecker;
+ llvm::raw_svector_ostream ToNothing(ImportChecker);
+ ToCtx.getTranslationUnitDecl()->print(ToNothing);
+
+ // This traverses the AST to catch certain bugs like poorly or not
+ // implemented subtrees.
+ Imported->dump(ToNothing);
+
+ return Imported;
}
-template<typename NodeType, typename MatcherType>
+const StringRef DeclToImportID = "declToImport";
+const StringRef DeclToVerifyID = "declToVerify";
+
+template <typename NodeType>
testing::AssertionResult
testImport(const std::string &FromCode, const ArgVector &FromArgs,
const std::string &ToCode, const ArgVector &ToArgs,
- MatchVerifier<NodeType> &Verifier, const MatcherType &AMatcher) {
+ MatchVerifier<NodeType> &Verifier,
+ const BindableMatcher<NodeType> &SearchMatcher,
+ const BindableMatcher<NodeType> &VerificationMatcher) {
const char *const InputFileName = "input.cc";
const char *const OutputFileName = "output.cc";
@@ -97,46 +141,47 @@ testImport(const std::string &FromCode, const ArgVector &FromArgs,
ASTContext &FromCtx = FromAST->getASTContext(),
&ToCtx = ToAST->getASTContext();
- // Add input.cc to virtual file system so importer can 'find' it
- // while importing SourceLocations.
- createVirtualFileIfNeeded(ToAST.get(), InputFileName, FromCode);
-
ASTImporter Importer(ToCtx, ToAST->getFileManager(),
FromCtx, FromAST->getFileManager(), false);
- IdentifierInfo *ImportedII = &FromCtx.Idents.get("declToImport");
- assert(ImportedII && "Declaration with 'declToImport' name"
- "should be specified in test!");
- DeclarationName ImportDeclName(ImportedII);
- SmallVector<NamedDecl *, 4> FoundDecls;
- FromCtx.getTranslationUnitDecl()->localUncachedLookup(
- ImportDeclName, FoundDecls);
+ auto FoundNodes = match(SearchMatcher, FromCtx);
+ if (FoundNodes.size() != 1)
+ return testing::AssertionFailure()
+ << "Multiple potential nodes were found!";
- if (FoundDecls.size() != 1)
- return testing::AssertionFailure() << "Multiple declarations were found!";
+ auto ToImport = selectFirst<NodeType>(DeclToImportID, FoundNodes);
+ if (!ToImport)
+ return testing::AssertionFailure() << "Node type mismatch!";
// Sanity check: the node being imported should match in the same way as
// the result node.
- EXPECT_TRUE(Verifier.match(FoundDecls.front(), AMatcher));
+ BindableMatcher<NodeType> WrapperMatcher(VerificationMatcher);
+ EXPECT_TRUE(Verifier.match(ToImport, WrapperMatcher));
- auto Imported = Importer.Import(FoundDecls.front());
+ auto Imported = importNode(FromAST.get(), ToAST.get(), Importer, ToImport);
if (!Imported)
return testing::AssertionFailure() << "Import failed, nullptr returned!";
- // This should dump source locations and assert if some source locations
- // were not imported.
- SmallString<1024> ImportChecker;
- llvm::raw_svector_ostream ToNothing(ImportChecker);
- ToCtx.getTranslationUnitDecl()->print(ToNothing);
-
- // This traverses the AST to catch certain bugs like poorly or not
- // implemented subtrees.
- Imported->dump(ToNothing);
-
- return Verifier.match(Imported, AMatcher);
+ return Verifier.match(Imported, WrapperMatcher);
}
-template<typename NodeType, typename MatcherType>
+template <typename NodeType>
+testing::AssertionResult
+testImport(const std::string &FromCode, const ArgVector &FromArgs,
+ const std::string &ToCode, const ArgVector &ToArgs,
+ MatchVerifier<NodeType> &Verifier,
+ const BindableMatcher<NodeType> &VerificationMatcher) {
+ return testImport(
+ FromCode, FromArgs, ToCode, ToArgs, Verifier,
+ translationUnitDecl(
+ has(namedDecl(hasName(DeclToImportID)).bind(DeclToImportID))),
+ VerificationMatcher);
+}
+
+/// Test how AST node named "declToImport" located in the translation unit
+/// of "FromCode" virtual file is imported to "ToCode" virtual file.
+/// The verification is done by running AMatcher over the imported node.
+template <typename NodeType, typename MatcherType>
void testImport(const std::string &FromCode, Language FromLang,
const std::string &ToCode, Language ToLang,
MatchVerifier<NodeType> &Verifier,
@@ -149,8 +194,6 @@ void testImport(const std::string &FromCode, Language FromLang,
Verifier, AMatcher));
}
-const StringRef DeclToImportID = "declToImport";
-
// This class provides generic methods to write tests which can check internal
// attributes of AST nodes like getPreviousDecl(), isVirtual(), etc. Also,
// this fixture makes it possible to import from several "From" contexts.
@@ -164,8 +207,8 @@ class ASTImporterTestBase : public ::testing::TestWithParam<ArgVector> {
struct TU {
// Buffer for the context, must live in the test scope.
- std::string Code;
- std::string FileName;
+ StringRef Code;
+ StringRef FileName;
std::unique_ptr<ASTUnit> Unit;
TranslationUnitDecl *TUDecl = nullptr;
TU(StringRef Code, StringRef FileName, ArgVector Args)
@@ -297,16 +340,121 @@ public:
}
};
-AST_MATCHER_P(RecordDecl, hasFieldOrder, std::vector<StringRef>, Order) {
- size_t Index = 0;
- for (FieldDecl *Field : Node.fields()) {
- if (Index == Order.size())
- return false;
- if (Field->getName() != Order[Index])
- return false;
- ++Index;
+
+struct ImportAction {
+ StringRef FromFilename;
+ StringRef ToFilename;
+ // FIXME: Generalize this to support other node kinds.
+ BindableMatcher<Decl> ImportPredicate;
+
+ ImportAction(StringRef FromFilename, StringRef ToFilename,
+ DeclarationMatcher ImportPredicate)
+ : FromFilename(FromFilename), ToFilename(ToFilename),
+ ImportPredicate(ImportPredicate) {}
+
+ ImportAction(StringRef FromFilename, StringRef ToFilename,
+ const std::string &DeclName)
+ : FromFilename(FromFilename), ToFilename(ToFilename),
+ ImportPredicate(namedDecl(hasName(DeclName))) {}
+};
+
+using SingleASTUnitForAllOpts = std::vector<std::unique_ptr<ASTUnit>>;
+using AllASTUnitsForAllOpts = StringMap<SingleASTUnitForAllOpts>;
+
+struct CodeEntry {
+ std::string CodeSample;
+ Language Lang;
+
+ /// Builds N copies of ASTUnits for each potential compile options set
+ /// for further import actions. N is equal to size of this option set.
+ SingleASTUnitForAllOpts createASTUnits(StringRef FileName) const {
+ auto RunOpts = getRunOptionsForLanguage(Lang);
+ size_t NumOpts = RunOpts.size();
+ SingleASTUnitForAllOpts ResultASTs(NumOpts);
+ for (size_t CompileOpt = 0; CompileOpt < NumOpts; ++CompileOpt) {
+ auto AST = tooling::buildASTFromCodeWithArgs(
+ CodeSample, RunOpts[CompileOpt], FileName);
+ EXPECT_TRUE(AST.get());
+ ResultASTs[CompileOpt] = std::move(AST);
+ }
+ return ResultASTs;
+ }
+};
+
+using CodeFiles = StringMap<CodeEntry>;
+
+/// Test an arbitrary sequence of imports for a set of given in-memory files.
+/// The verification is done by running VerificationMatcher against a specified
+/// AST node inside of one of given files.
+/// \param CodeSamples Map whose key is the file name and the value is the file
+/// content.
+/// \param ImportActions Sequence of imports. Each import in sequence
+/// specifies "from file" and "to file" and a matcher that is used for
+/// searching a declaration for import in "from file".
+/// \param FileForFinalCheck Name of virtual file for which the final check is
+/// applied.
+/// \param FinalSelectPredicate Matcher that specifies the AST node in the
+/// FileForFinalCheck for which the verification will be done.
+/// \param VerificationMatcher Matcher that will be used for verification after
+/// all imports in sequence are done.
+void testImportSequence(const CodeFiles &CodeSamples,
+ const std::vector<ImportAction> &ImportActions,
+ StringRef FileForFinalCheck,
+ BindableMatcher<Decl> FinalSelectPredicate,
+ BindableMatcher<Decl> VerificationMatcher) {
+ AllASTUnitsForAllOpts AllASTUnits;
+ using ImporterKey = std::pair<const ASTUnit *, const ASTUnit *>;
+ llvm::DenseMap<ImporterKey, std::unique_ptr<ASTImporter>> Importers;
+
+ auto GenASTsIfNeeded = [&AllASTUnits, &CodeSamples](StringRef Filename) {
+ if (!AllASTUnits.count(Filename)) {
+ auto Found = CodeSamples.find(Filename);
+ assert(Found != CodeSamples.end() && "Wrong file for import!");
+ AllASTUnits[Filename] = Found->getValue().createASTUnits(Filename);
+ }
+ };
+
+ size_t NumCompileOpts = 0;
+ for (const ImportAction &Action : ImportActions) {
+ StringRef FromFile = Action.FromFilename, ToFile = Action.ToFilename;
+ GenASTsIfNeeded(FromFile);
+ GenASTsIfNeeded(ToFile);
+ NumCompileOpts = AllASTUnits[FromFile].size();
+
+ for (size_t CompileOpt = 0; CompileOpt < NumCompileOpts; ++CompileOpt) {
+ ASTUnit *From = AllASTUnits[FromFile][CompileOpt].get();
+ ASTUnit *To = AllASTUnits[ToFile][CompileOpt].get();
+
+ // Create a new importer if needed.
+ std::unique_ptr<ASTImporter> &ImporterRef = Importers[{From, To}];
+ if (!ImporterRef)
+ ImporterRef.reset(new ASTImporter(
+ To->getASTContext(), To->getFileManager(), From->getASTContext(),
+ From->getFileManager(), false));
+
+ // Find the declaration and import it.
+ auto FoundDecl = match(Action.ImportPredicate.bind(DeclToImportID),
+ From->getASTContext());
+ EXPECT_TRUE(FoundDecl.size() == 1);
+ const Decl *ToImport = selectFirst<Decl>(DeclToImportID, FoundDecl);
+ auto Imported = importNode(From, To, *ImporterRef, ToImport);
+ EXPECT_TRUE(Imported);
+ }
+ }
+
+ // NOTE: We don't do cross-option import check here due to fast growth of
+ // potential option sets.
+ for (size_t CompileOpt = 0; CompileOpt < NumCompileOpts; ++CompileOpt) {
+ // Find the declaration and import it.
+ auto FoundDecl =
+ match(FinalSelectPredicate.bind(DeclToVerifyID),
+ AllASTUnits[FileForFinalCheck][CompileOpt]->getASTContext());
+ EXPECT_TRUE(FoundDecl.size() == 1);
+ const Decl *ToVerify = selectFirst<Decl>(DeclToVerifyID, FoundDecl);
+ MatchVerifier<Decl> Verifier;
+ EXPECT_TRUE(Verifier.match(ToVerify,
+ BindableMatcher<Decl>(VerificationMatcher)));
}
- return Index == Order.size();
}
TEST(ImportExpr, ImportStringLiteral) {
@@ -1127,6 +1275,18 @@ TEST_P(
MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
}
+AST_MATCHER_P(RecordDecl, hasFieldOrder, std::vector<StringRef>, Order) {
+ size_t Index = 0;
+ for (FieldDecl *Field : Node.fields()) {
+ if (Index == Order.size())
+ return false;
+ if (Field->getName() != Order[Index])
+ return false;
+ ++Index;
+ }
+ return Index == Order.size();
+}
+
TEST_P(ASTImporterTestBase,
TUshouldContainClassTemplateSpecializationOfExplicitInstantiation) {
Decl *From, *To;
@@ -1503,5 +1663,51 @@ INSTANTIATE_TEST_CASE_P(
ParameterizedTests, ImportFunctions,
::testing::Values(ArgVector(), ArgVector{"-fdelayed-template-parsing"}),);
+AST_MATCHER_P(TagDecl, hasTypedefForAnonDecl, Matcher<TypedefNameDecl>,
+ InnerMatcher) {
+ if (auto *Typedef = Node.getTypedefNameForAnonDecl())
+ return InnerMatcher.matches(*Typedef, Finder, Builder);
+ return false;
+}
+
+TEST(ImportDecl, ImportEnumSequential) {
+ CodeFiles Samples{{"main.c",
+ {"void foo();"
+ "void moo();"
+ "int main() { foo(); moo(); }",
+ Lang_C}},
+
+ {"foo.c",
+ {"typedef enum { THING_VALUE } thing_t;"
+ "void conflict(thing_t type);"
+ "void foo() { (void)THING_VALUE; }"
+ "void conflict(thing_t type) {}",
+ Lang_C}},
+
+ {"moo.c",
+ {"typedef enum { THING_VALUE } thing_t;"
+ "void conflict(thing_t type);"
+ "void moo() { conflict(THING_VALUE); }",
+ Lang_C}}};
+
+ auto VerificationMatcher =
+ enumDecl(has(enumConstantDecl(hasName("THING_VALUE"))),
+ hasTypedefForAnonDecl(hasName("thing_t")));
+
+ ImportAction ImportFoo{"foo.c", "main.c", functionDecl(hasName("foo"))},
+ ImportMoo{"moo.c", "main.c", functionDecl(hasName("moo"))};
+
+ testImportSequence(
+ Samples, {ImportFoo, ImportMoo}, // "foo", them "moo".
+ // Just check that there is only one enum decl in the result AST.
+ "main.c", enumDecl(), VerificationMatcher);
+
+ // For different import order, result should be the same.
+ testImportSequence(
+ Samples, {ImportMoo, ImportFoo}, // "moo", them "foo".
+ // Check that there is only one enum decl in the result AST.
+ "main.c", enumDecl(), VerificationMatcher);
+}
+
} // end namespace ast_matchers
} // end namespace clang
OpenPOWER on IntegriCloud