From: Argyrios Kyrtzidis Date: Mon, 5 Mar 2012 05:48:17 +0000 (+0000) Subject: [preprocessor] Enhance PreprocessingRecord to keep track of locations of conditional... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c6c54521f95760a5eaf29b668d4bf41fe2af49d7;p=clang [preprocessor] Enhance PreprocessingRecord to keep track of locations of conditional directives. Introduce PreprocessingRecord::rangeIntersectsConditionalDirective() which returns true if a given range intersects with a conditional directive block. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152018 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Frontend/PreprocessorOptions.h b/include/clang/Frontend/PreprocessorOptions.h index e36669d384..652c24d298 100644 --- a/include/clang/Frontend/PreprocessorOptions.h +++ b/include/clang/Frontend/PreprocessorOptions.h @@ -49,6 +49,10 @@ public: unsigned DetailedRecord : 1; /// Whether we should maintain a detailed /// record of all macro definitions and /// expansions. + unsigned DetailedRecordConditionalDirectives : 1; /// Whether in the + /// preprocessing record we should also keep + /// track of locations of conditional directives + /// in non-system files. /// The implicit PCH included at the start of the translation unit, or empty. std::string ImplicitPCHInclude; @@ -158,6 +162,7 @@ public: public: PreprocessorOptions() : UsePredefines(true), DetailedRecord(false), + DetailedRecordConditionalDirectives(false), DisablePCHValidation(false), DisableStatCache(false), DumpDeserializedPCHDecls(false), PrecompiledPreambleBytes(0, true), diff --git a/include/clang/Lex/PreprocessingRecord.h b/include/clang/Lex/PreprocessingRecord.h index a778bbe031..587c2a7298 100644 --- a/include/clang/Lex/PreprocessingRecord.h +++ b/include/clang/Lex/PreprocessingRecord.h @@ -17,6 +17,7 @@ #include "clang/Lex/PPCallbacks.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/IdentifierTable.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/Allocator.h" @@ -299,6 +300,44 @@ namespace clang { /// and are referenced by the iterator using negative indices. std::vector LoadedPreprocessedEntities; + bool RecordCondDirectives; + unsigned CondDirectiveNextIdx; + SmallVector CondDirectiveStack; + + class CondDirectiveLoc { + SourceLocation Loc; + unsigned Idx; + + public: + CondDirectiveLoc(SourceLocation Loc, unsigned Idx) : Loc(Loc), Idx(Idx) {} + + SourceLocation getLoc() const { return Loc; } + unsigned getIdx() const { return Idx; } + + class Comp { + SourceManager &SM; + public: + explicit Comp(SourceManager &SM) : SM(SM) {} + bool operator()(const CondDirectiveLoc &LHS, + const CondDirectiveLoc &RHS) { + return SM.isBeforeInTranslationUnit(LHS.getLoc(), RHS.getLoc()); + } + bool operator()(const CondDirectiveLoc &LHS, SourceLocation RHS) { + return SM.isBeforeInTranslationUnit(LHS.getLoc(), RHS); + } + bool operator()(SourceLocation LHS, const CondDirectiveLoc &RHS) { + return SM.isBeforeInTranslationUnit(LHS, RHS.getLoc()); + } + }; + }; + + typedef std::vector CondDirectiveLocsTy; + /// \brief The locations of conditional directives in source order. + CondDirectiveLocsTy CondDirectiveLocs; + + void addCondDirectiveLoc(CondDirectiveLoc DirLoc); + unsigned findCondDirectiveIdx(SourceLocation Loc) const; + /// \brief Global (loaded or local) ID for a preprocessed entity. /// Negative values are used to indicate preprocessed entities /// loaded from the external source while non-negative values are used to @@ -349,7 +388,7 @@ namespace clang { public: /// \brief Construct a new preprocessing record. - explicit PreprocessingRecord(SourceManager &SM); + PreprocessingRecord(SourceManager &SM, bool RecordConditionalDirectives); /// \brief Allocate memory in the preprocessing record. void *Allocate(unsigned Size, unsigned Align = 8) { @@ -517,7 +556,25 @@ namespace clang { /// \brief Add a new preprocessed entity to this record. void addPreprocessedEntity(PreprocessedEntity *Entity); - + + /// \brief Returns true if this PreprocessingRecord is keeping track of + /// conditional directives locations. + bool isRecordingConditionalDirectives() const { + return RecordCondDirectives; + } + + /// \brief Returns true if the given range intersects with a conditional + /// directive. if a #if/#endif block is fully contained within the range, + /// this function will return false. + bool rangeIntersectsConditionalDirective(SourceRange Range) const; + + /// \brief Returns true if the given locations are in different regions, + /// separated by conditional directive blocks. + bool areInDifferentConditionalDirectiveRegion(SourceLocation LHS, + SourceLocation RHS) const { + return findCondDirectiveIdx(LHS) != findCondDirectiveIdx(RHS); + } + /// \brief Set the external source for preprocessed entities. void SetExternalSource(ExternalPreprocessingRecordSource &Source); @@ -530,6 +587,7 @@ namespace clang { /// \c MacroInfo. MacroDefinition *findMacroDefinition(const MacroInfo *MI); + private: virtual void MacroExpands(const Token &Id, const MacroInfo* MI, SourceRange Range); virtual void MacroDefined(const Token &Id, const MacroInfo *MI); @@ -542,8 +600,14 @@ namespace clang { SourceLocation EndLoc, StringRef SearchPath, StringRef RelativePath); + virtual void If(SourceLocation Loc, SourceRange ConditionRange); + virtual void Elif(SourceLocation Loc, SourceRange ConditionRange, + SourceLocation IfLoc); + virtual void Ifdef(SourceLocation Loc, const Token &MacroNameTok); + virtual void Ifndef(SourceLocation Loc, const Token &MacroNameTok); + virtual void Else(SourceLocation Loc, SourceLocation IfLoc); + virtual void Endif(SourceLocation Loc, SourceLocation IfLoc); - private: /// \brief Cached result of the last \see getPreprocessedEntitiesInRange /// query. struct { diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 9ee9e82529..c5bccb1171 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -512,7 +512,7 @@ public: /// \brief Create a new preprocessing record, which will keep track of /// all macro expansions, macro definitions, etc. - void createPreprocessingRecord(); + void createPreprocessingRecord(bool RecordConditionalDirectives); /// EnterMainSourceFile - Enter the specified FileID as the main source file, /// which implicitly adds the builtin defines etc. diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index bf3e3a8908..c6838be7af 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -257,7 +257,7 @@ void CompilerInstance::createPreprocessor() { } if (PPOpts.DetailedRecord) - PP->createPreprocessingRecord(); + PP->createPreprocessingRecord(PPOpts.DetailedRecordConditionalDirectives); InitializePreprocessor(*PP, PPOpts, getHeaderSearchOpts(), getFrontendOpts()); diff --git a/lib/Lex/PreprocessingRecord.cpp b/lib/Lex/PreprocessingRecord.cpp index c87088d2de..b8e6152466 100644 --- a/lib/Lex/PreprocessingRecord.cpp +++ b/lib/Lex/PreprocessingRecord.cpp @@ -37,9 +37,14 @@ InclusionDirective::InclusionDirective(PreprocessingRecord &PPRec, this->FileName = StringRef(Memory, FileName.size()); } -PreprocessingRecord::PreprocessingRecord(SourceManager &SM) - : SourceMgr(SM), ExternalSource(0) +PreprocessingRecord::PreprocessingRecord(SourceManager &SM, + bool RecordConditionalDirectives) + : SourceMgr(SM), + RecordCondDirectives(RecordConditionalDirectives), CondDirectiveNextIdx(0), + ExternalSource(0) { + if (RecordCondDirectives) + CondDirectiveStack.push_back(CondDirectiveNextIdx++); } /// \brief Returns a pair of [Begin, End) iterators of preprocessed entities @@ -397,6 +402,95 @@ void PreprocessingRecord::InclusionDirective( addPreprocessedEntity(ID); } +bool PreprocessingRecord::rangeIntersectsConditionalDirective( + SourceRange Range) const { + if (Range.isInvalid()) + return false; + + CondDirectiveLocsTy::const_iterator + low = std::lower_bound(CondDirectiveLocs.begin(), CondDirectiveLocs.end(), + Range.getBegin(), CondDirectiveLoc::Comp(SourceMgr)); + if (low == CondDirectiveLocs.end()) + return false; + + if (SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), low->getLoc())) + return false; + + CondDirectiveLocsTy::const_iterator + upp = std::upper_bound(low, CondDirectiveLocs.end(), + Range.getEnd(), CondDirectiveLoc::Comp(SourceMgr)); + unsigned uppIdx; + if (upp != CondDirectiveLocs.end()) + uppIdx = upp->getIdx(); + else + uppIdx = 0; + + return low->getIdx() != uppIdx; +} + +unsigned PreprocessingRecord::findCondDirectiveIdx(SourceLocation Loc) const { + if (Loc.isInvalid()) + return 0; + + CondDirectiveLocsTy::const_iterator + low = std::lower_bound(CondDirectiveLocs.begin(), CondDirectiveLocs.end(), + Loc, CondDirectiveLoc::Comp(SourceMgr)); + if (low == CondDirectiveLocs.end()) + return 0; + return low->getIdx(); +} + +void PreprocessingRecord::addCondDirectiveLoc(CondDirectiveLoc DirLoc) { + // Ignore directives in system headers. + if (SourceMgr.isInSystemHeader(DirLoc.getLoc())) + return; + + assert(CondDirectiveLocs.empty() || + SourceMgr.isBeforeInTranslationUnit(CondDirectiveLocs.back().getLoc(), + DirLoc.getLoc())); + CondDirectiveLocs.push_back(DirLoc); +} + +void PreprocessingRecord::If(SourceLocation Loc, SourceRange ConditionRange) { + if (RecordCondDirectives) { + addCondDirectiveLoc(CondDirectiveLoc(Loc, CondDirectiveStack.back())); + CondDirectiveStack.push_back(CondDirectiveNextIdx++); + } +} + +void PreprocessingRecord::Ifdef(SourceLocation Loc, const Token &MacroNameTok) { + if (RecordCondDirectives) { + addCondDirectiveLoc(CondDirectiveLoc(Loc, CondDirectiveStack.back())); + CondDirectiveStack.push_back(CondDirectiveNextIdx++); + } +} + +void PreprocessingRecord::Ifndef(SourceLocation Loc,const Token &MacroNameTok) { + if (RecordCondDirectives) { + addCondDirectiveLoc(CondDirectiveLoc(Loc, CondDirectiveStack.back())); + CondDirectiveStack.push_back(CondDirectiveNextIdx++); + } +} + +void PreprocessingRecord::Elif(SourceLocation Loc, SourceRange ConditionRange, + SourceLocation IfLoc) { + if (RecordCondDirectives) + addCondDirectiveLoc(CondDirectiveLoc(Loc, CondDirectiveStack.back())); +} + +void PreprocessingRecord::Else(SourceLocation Loc, SourceLocation IfLoc) { + if (RecordCondDirectives) + addCondDirectiveLoc(CondDirectiveLoc(Loc, CondDirectiveStack.back())); +} + +void PreprocessingRecord::Endif(SourceLocation Loc, SourceLocation IfLoc) { + if (RecordCondDirectives) { + addCondDirectiveLoc(CondDirectiveLoc(Loc, CondDirectiveStack.back())); + assert(!CondDirectiveStack.empty()); + CondDirectiveStack.pop_back(); + } +} + size_t PreprocessingRecord::getTotalMemory() const { return BumpAlloc.getTotalMemory() + llvm::capacity_in_bytes(MacroDefinitions) diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index b6ea65de49..06914c7cdc 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -654,10 +654,11 @@ CommentHandler::~CommentHandler() { } CodeCompletionHandler::~CodeCompletionHandler() { } -void Preprocessor::createPreprocessingRecord() { +void Preprocessor::createPreprocessingRecord(bool RecordConditionalDirectives) { if (Record) return; - Record = new PreprocessingRecord(getSourceManager()); + Record = new PreprocessingRecord(getSourceManager(), + RecordConditionalDirectives); addPPCallbacks(Record); } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index e1404bf5f3..39f24da6d4 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -1744,7 +1744,7 @@ ASTReader::ReadASTBlock(ModuleFile &F) { = F.PreprocessorDetailCursor.GetCurrentBitNo(); if (!PP.getPreprocessingRecord()) - PP.createPreprocessingRecord(); + PP.createPreprocessingRecord(/*RecordConditionalDirectives=*/false); if (!PP.getPreprocessingRecord()->getExternalSource()) PP.getPreprocessingRecord()->SetExternalSource(*this); break; @@ -2288,7 +2288,7 @@ ASTReader::ReadASTBlock(ModuleFile &F) { unsigned StartingID; if (!PP.getPreprocessingRecord()) - PP.createPreprocessingRecord(); + PP.createPreprocessingRecord(/*RecordConditionalDirectives=*/false); if (!PP.getPreprocessingRecord()->getExternalSource()) PP.getPreprocessingRecord()->SetExternalSource(*this); StartingID diff --git a/unittests/Lex/PreprocessingRecordTest.cpp b/unittests/Lex/PreprocessingRecordTest.cpp new file mode 100644 index 0000000000..5b5d933d1b --- /dev/null +++ b/unittests/Lex/PreprocessingRecordTest.cpp @@ -0,0 +1,139 @@ +//===- unittests/Lex/PreprocessingRecordTest.cpp - PreprocessingRecord tests =// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Lex/ModuleLoader.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessingRecord.h" +#include "llvm/Config/config.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { + +// The test fixture. +class PreprocessingRecordTest : public ::testing::Test { +protected: + PreprocessingRecordTest() + : FileMgr(FileMgrOpts), + DiagID(new DiagnosticIDs()), + Diags(DiagID, new IgnoringDiagConsumer()), + SourceMgr(Diags, FileMgr) { + TargetOpts.Triple = "x86_64-apple-darwin11.1.0"; + Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); + } + + FileSystemOptions FileMgrOpts; + FileManager FileMgr; + IntrusiveRefCntPtr DiagID; + DiagnosticsEngine Diags; + SourceManager SourceMgr; + LangOptions LangOpts; + TargetOptions TargetOpts; + IntrusiveRefCntPtr Target; +}; + +class VoidModuleLoader : public ModuleLoader { + virtual Module *loadModule(SourceLocation ImportLoc, ModuleIdPath Path, + Module::NameVisibilityKind Visibility, + bool IsInclusionDirective) { + return 0; + } +}; + +TEST_F(PreprocessingRecordTest, PPRecAPI) { + const char *source = + "0 1\n" + "#if 1\n" + "2\n" + "#ifndef BB\n" + "3 4\n" + "#else\n" + "#endif\n" + "5\n" + "#endif\n" + "6\n" + "#if 1\n" + "7\n" + "#if 1\n" + "#endif\n" + "8\n" + "#endif\n" + "9\n"; + + MemoryBuffer *buf = MemoryBuffer::getMemBuffer(source); + SourceMgr.createMainFileIDForMemBuffer(buf); + + VoidModuleLoader ModLoader; + HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, Target.getPtr()); + Preprocessor PP(Diags, LangOpts, + Target.getPtr(), + SourceMgr, HeaderInfo, ModLoader, + /*IILookup =*/ 0, + /*OwnsHeaderSearch =*/false, + /*DelayInitialization =*/ false); + PP.createPreprocessingRecord(true); + PP.EnterMainSourceFile(); + + std::vector toks; + while (1) { + Token tok; + PP.Lex(tok); + if (tok.is(tok::eof)) + break; + toks.push_back(tok); + } + + // Make sure we got the tokens that we expected. + ASSERT_EQ(10U, toks.size()); + + PreprocessingRecord &PPRec = *PP.getPreprocessingRecord(); + EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[0].getLocation(), toks[1].getLocation()))); + EXPECT_TRUE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[0].getLocation(), toks[2].getLocation()))); + EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[3].getLocation(), toks[4].getLocation()))); + EXPECT_TRUE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[1].getLocation(), toks[5].getLocation()))); + EXPECT_TRUE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[2].getLocation(), toks[6].getLocation()))); + EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[2].getLocation(), toks[5].getLocation()))); + EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[0].getLocation(), toks[6].getLocation()))); + EXPECT_TRUE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[2].getLocation(), toks[8].getLocation()))); + EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[0].getLocation(), toks[9].getLocation()))); + + EXPECT_TRUE(PPRec.areInDifferentConditionalDirectiveRegion( + toks[0].getLocation(), toks[2].getLocation())); + EXPECT_FALSE(PPRec.areInDifferentConditionalDirectiveRegion( + toks[3].getLocation(), toks[4].getLocation())); + EXPECT_TRUE(PPRec.areInDifferentConditionalDirectiveRegion( + toks[1].getLocation(), toks[5].getLocation())); + EXPECT_TRUE(PPRec.areInDifferentConditionalDirectiveRegion( + toks[2].getLocation(), toks[0].getLocation())); + EXPECT_FALSE(PPRec.areInDifferentConditionalDirectiveRegion( + toks[4].getLocation(), toks[3].getLocation())); + EXPECT_TRUE(PPRec.areInDifferentConditionalDirectiveRegion( + toks[5].getLocation(), toks[1].getLocation())); +} + +} // anonymous namespace