[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 e36669d..652c24d 100644
--- a/include/clang/Frontend/PreprocessorOptions.h
+++ b/include/clang/Frontend/PreprocessorOptions.h
@@ -49,6 +49,10 @@
   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:
   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 a778bbe..587c2a7 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 @@
     /// and are referenced by the iterator using negative indices.
     std::vector<PreprocessedEntity *> LoadedPreprocessedEntities;
 
+    bool RecordCondDirectives;
+    unsigned CondDirectiveNextIdx;
+    SmallVector<unsigned, 6> 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<CondDirectiveLoc> 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 @@
     
   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 @@
 
     /// \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 @@
     /// \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 @@
                                     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 9ee9e82..c5bccb1 100644
--- a/include/clang/Lex/Preprocessor.h
+++ b/include/clang/Lex/Preprocessor.h
@@ -512,7 +512,7 @@
 
   /// \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 bf3e3a8..c6838be 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -257,7 +257,7 @@
   }
 
   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 c87088d..b8e6152 100644
--- a/lib/Lex/PreprocessingRecord.cpp
+++ b/lib/Lex/PreprocessingRecord.cpp
@@ -37,9 +37,14 @@
   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 @@
   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 b6ea65d..06914c7 100644
--- a/lib/Lex/Preprocessor.cpp
+++ b/lib/Lex/Preprocessor.cpp
@@ -654,10 +654,11 @@
 
 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 e1404bf..39f24da 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -1744,7 +1744,7 @@
           = 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 @@
       
       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 0000000..5b5d933
--- /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<DiagnosticIDs> DiagID;
+  DiagnosticsEngine Diags;
+  SourceManager SourceMgr;
+  LangOptions LangOpts;
+  TargetOptions TargetOpts;
+  IntrusiveRefCntPtr<TargetInfo> 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<Token> 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