juliehockett updated this revision to Diff 137245.
juliehockett marked 8 inline comments as done.
juliehockett added a comment.

Adding in support for mapper tests and addressing comments.


https://reviews.llvm.org/D43341

Files:
  clang-doc/BitcodeReader.cpp
  clang-doc/BitcodeReader.h
  clang-doc/BitcodeWriter.cpp
  clang-doc/BitcodeWriter.h
  clang-doc/CMakeLists.txt
  clang-doc/Reducer.cpp
  clang-doc/Reducer.h
  clang-doc/Representation.cpp
  clang-doc/Representation.h
  clang-doc/tool/ClangDocMain.cpp
  test/clang-doc/bc-comment.cpp
  test/clang-doc/bc-namespace.cpp
  test/clang-doc/bc-record.cpp

Index: test/clang-doc/bc-record.cpp
===================================================================
--- /dev/null
+++ test/clang-doc/bc-record.cpp
@@ -0,0 +1,168 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "" > %t/compile_flags.txt
+// RUN: cp "%s" "%t/test.cpp"
+// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs
+// RUN: llvm-bcanalyzer %t/docs/bc/docs.bc --dump | FileCheck %s
+
+union A { int X; int Y; };
+
+enum B { X, Y };
+
+enum class Bc { A, B };
+
+struct C { int i; };
+
+class D {};
+
+class E {
+public:
+  E() {}
+  ~E() {}
+
+protected:
+  void ProtectedMethod();
+};
+
+void E::ProtectedMethod() {}
+
+class F : virtual private D, public E {};
+
+class X {
+  class Y {};
+};
+
+void H() {
+  class I {};
+}
+
+// CHECK: <BLOCKINFO_BLOCK/>
+// CHECK-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
+  // CHECK-NEXT: <Version abbrevid=4 op0=1/>
+// CHECK-NEXT: </VersionBlock>
+// CHECK-NEXT: <FunctionBlock NumWords=44 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = 'DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'E'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=1 op1=40/> blob data = '289584A8E0FF4178A794622A547AA622503967A1'
+  // CHECK-NEXT: <IsMethod abbrevid=11 op0=1/>
+  // CHECK-NEXT: <Parent abbrevid=9 op0=1 op1=40/> blob data = '289584A8E0FF4178A794622A547AA622503967A1'
+  // CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=4/> blob data = 'void'
+  // CHECK-NEXT: </TypeBlock>
+// CHECK-NEXT: </FunctionBlock>
+// CHECK-NEXT: <FunctionBlock NumWords=44 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = 'BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17'
+  // CHECK-NEXT: <Name abbrevid=5 op0=2/> blob data = '~E'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=1 op1=40/> blob data = '289584A8E0FF4178A794622A547AA622503967A1'
+  // CHECK-NEXT: <IsMethod abbrevid=11 op0=1/>
+  // CHECK-NEXT: <Parent abbrevid=9 op0=1 op1=40/> blob data = '289584A8E0FF4178A794622A547AA622503967A1'
+  // CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=4/> blob data = 'void'
+  // CHECK-NEXT: </TypeBlock>
+// CHECK-NEXT: </FunctionBlock>
+// CHECK-NEXT: <FunctionBlock NumWords=47 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '5093D428CDC62096A67547BA52566E4FB9404EEE'
+  // CHECK-NEXT: <Name abbrevid=5 op0=15/> blob data = 'ProtectedMethod'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=1 op1=40/> blob data = '289584A8E0FF4178A794622A547AA622503967A1'
+  // CHECK-NEXT: <IsMethod abbrevid=11 op0=1/>
+  // CHECK-NEXT: <Parent abbrevid=9 op0=1 op1=40/> blob data = '289584A8E0FF4178A794622A547AA622503967A1'
+  // CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=4/> blob data = 'void'
+  // CHECK-NEXT: </TypeBlock>
+// CHECK-NEXT: </FunctionBlock>
+// CHECK-NEXT: <FunctionBlock NumWords=47 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '5093D428CDC62096A67547BA52566E4FB9404EEE'
+  // CHECK-NEXT: <Name abbrevid=5 op0=15/> blob data = 'ProtectedMethod'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=1 op1=40/> blob data = '289584A8E0FF4178A794622A547AA622503967A1'
+  // CHECK-NEXT: <IsMethod abbrevid=11 op0=1/>
+  // CHECK-NEXT: <Parent abbrevid=9 op0=1 op1=40/> blob data = '289584A8E0FF4178A794622A547AA622503967A1'
+  // CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=4/> blob data = 'void'
+  // CHECK-NEXT: </TypeBlock>
+// CHECK-NEXT: </FunctionBlock>
+// CHECK-NEXT: <FunctionBlock NumWords=20 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'H'
+  // CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=4/> blob data = 'void'
+  // CHECK-NEXT: </TypeBlock>
+// CHECK-NEXT: </FunctionBlock>
+// CHECK-NEXT: <RecordBlock NumWords=31 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = 'ACE81AFA6627B4CEF2B456FB6E1252925674AF7E'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'A'
+  // CHECK-NEXT: <TagType abbrevid=9 op0=2/>
+  // CHECK-NEXT: <MemberTypeBlock NumWords=6 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
+    // CHECK-NEXT: <Name abbrevid=5 op0=4/> blob data = 'A::X'
+    // CHECK-NEXT: <Access abbrevid=6 op0=3/>
+  // CHECK-NEXT: </MemberTypeBlock>
+  // CHECK-NEXT: <MemberTypeBlock NumWords=6 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
+    // CHECK-NEXT: <Name abbrevid=5 op0=4/> blob data = 'A::Y'
+    // CHECK-NEXT: <Access abbrevid=6 op0=3/>
+  // CHECK-NEXT: </MemberTypeBlock>
+// CHECK-NEXT: </RecordBlock>
+// CHECK-NEXT: <RecordBlock NumWords=22 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '06B5F6A19BA9F6A832E127C9968282B94619B210'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'C'
+  // CHECK-NEXT: <MemberTypeBlock NumWords=6 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
+    // CHECK-NEXT: <Name abbrevid=5 op0=4/> blob data = 'C::i'
+    // CHECK-NEXT: <Access abbrevid=6 op0=3/>
+  // CHECK-NEXT: </MemberTypeBlock>
+// CHECK-NEXT: </RecordBlock>
+// CHECK-NEXT: <RecordBlock NumWords=14 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '0921737541208B8FA9BB42B60F78AC1D779AA054'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'D'
+  // CHECK-NEXT: <TagType abbrevid=9 op0=3/>
+// CHECK-NEXT: </RecordBlock>
+// CHECK-NEXT: <RecordBlock NumWords=14 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '289584A8E0FF4178A794622A547AA622503967A1'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'E'
+  // CHECK-NEXT: <TagType abbrevid=9 op0=3/>
+// CHECK-NEXT: </RecordBlock>
+// CHECK-NEXT: <RecordBlock NumWords=38 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = 'E3B54702FABFF4037025BA194FC27C47006330B5'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'F'
+  // CHECK-NEXT: <TagType abbrevid=9 op0=3/>
+  // CHECK-NEXT: <Parent abbrevid=10 op0=1 op1=40/> blob data = '289584A8E0FF4178A794622A547AA622503967A1'
+  // CHECK-NEXT: <VParent abbrevid=11 op0=1 op1=40/> blob data = '0921737541208B8FA9BB42B60F78AC1D779AA054'
+// CHECK-NEXT: </RecordBlock>
+// CHECK-NEXT: <RecordBlock NumWords=14 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'X'
+  // CHECK-NEXT: <TagType abbrevid=9 op0=3/>
+// CHECK-NEXT: </RecordBlock>
+// CHECK-NEXT: <RecordBlock NumWords=26 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '641AB4A3D36399954ACDE29C7A8833032BF40472'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'Y'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=1 op1=40/> blob data = 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E'
+  // CHECK-NEXT: <TagType abbrevid=9 op0=3/>
+// CHECK-NEXT: </RecordBlock>
+// CHECK-NEXT: <RecordBlock NumWords=26 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '126F2D5DA516D4A749E50A062AAF4296F8D69CE1'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'I'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=2 op1=40/> blob data = 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E'
+  // CHECK-NEXT: <TagType abbrevid=9 op0=3/>
+// CHECK-NEXT: </RecordBlock>
+// CHECK-NEXT: <EnumBlock NumWords=26 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = 'FC07BD34D5E77782C263FA944447929EA8753740'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'B'
+  // CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=3 op1=1/> blob data = 'X'
+  // CHECK-NEXT: </TypeBlock>
+  // CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=3 op1=1/> blob data = 'Y'
+  // CHECK-NEXT: </TypeBlock>
+// CHECK-NEXT: </EnumBlock>
+// CHECK-NEXT: <EnumBlock NumWords=28 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '1E3438A08BA22025C0B46289FF0686F92C8924C5'
+  // CHECK-NEXT: <Name abbrevid=5 op0=2/> blob data = 'Bc'
+  // CHECK-NEXT: <Scoped abbrevid=9 op0=1/>
+  // CHECK-NEXT: <TypeBlock NumWords=5 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=3 op1=5/> blob data = 'Bc::A'
+  // CHECK-NEXT: </TypeBlock>
+  // CHECK-NEXT: <TypeBlock NumWords=5 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=3 op1=5/> blob data = 'Bc::B'
+  // CHECK-NEXT: </TypeBlock>
+// CHECK-NEXT: </EnumBlock>
Index: test/clang-doc/bc-namespace.cpp
===================================================================
--- /dev/null
+++ test/clang-doc/bc-namespace.cpp
@@ -0,0 +1,81 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "" > %t/compile_flags.txt
+// RUN: cp "%s" "%t/test.cpp"
+// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs
+// RUN: llvm-bcanalyzer %t/docs/bc/docs.bc --dump | FileCheck %s
+
+namespace A {
+
+void f();
+
+}  // namespace A
+
+namespace A {
+
+void f(){};
+
+namespace B {
+
+enum E { X };
+
+E func(int i) { return X; }
+
+}  // namespace B
+}  // namespace A
+
+// CHECK: <BLOCKINFO_BLOCK/>
+// CHECK-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
+  // CHECK-NEXT: <Version abbrevid=4 op0=1/>
+// CHECK-NEXT: </VersionBlock>
+// CHECK-NEXT: <NamespaceBlock NumWords=14 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'A'
+// CHECK-NEXT: </NamespaceBlock>
+// CHECK-NEXT: <NamespaceBlock NumWords=14 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'A'
+// CHECK-NEXT: </NamespaceBlock>
+// CHECK-NEXT: <NamespaceBlock NumWords=26 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'B'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=0 op1=40/> blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C'
+// CHECK-NEXT: </NamespaceBlock>
+// CHECK-NEXT: <FunctionBlock NumWords=32 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'f'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=0 op1=40/> blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C'
+  // CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=4/> blob data = 'void'
+  // CHECK-NEXT: </TypeBlock>
+// CHECK-NEXT: </FunctionBlock>
+// CHECK-NEXT: <FunctionBlock NumWords=32 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'f'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=0 op1=40/> blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C'
+  // CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=4/> blob data = 'void'
+  // CHECK-NEXT: </TypeBlock>
+// CHECK-NEXT: </FunctionBlock>
+// CHECK-NEXT: <FunctionBlock NumWords=54 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '9A82CB33ED0FDF81EE383D31CD0957D153C5E840'
+  // CHECK-NEXT: <Name abbrevid=5 op0=4/> blob data = 'func'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=0 op1=40/> blob data = 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=0 op1=40/> blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C'
+  // CHECK-NEXT: <TypeBlock NumWords=6 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=12/> blob data = 'enum A::B::E'
+  // CHECK-NEXT: </TypeBlock>
+  // CHECK-NEXT: <FieldTypeBlock NumWords=6 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
+    // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'i'
+  // CHECK-NEXT: </FieldTypeBlock>
+// CHECK-NEXT: </FunctionBlock>
+// CHECK-NEXT: <EnumBlock NumWords=45 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = 'E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'E'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=0 op1=40/> blob data = 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5'
+  // CHECK-NEXT: <Namespace abbrevid=6 op0=0 op1=40/> blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C'
+  // CHECK-NEXT: <TypeBlock NumWords=5 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=3 op1=7/> blob data = 'A::B::X'
+  // CHECK-NEXT: </TypeBlock>
+// CHECK-NEXT: </EnumBlock>
Index: test/clang-doc/bc-comment.cpp
===================================================================
--- /dev/null
+++ test/clang-doc/bc-comment.cpp
@@ -0,0 +1,197 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "" > %t/compile_flags.txt
+// RUN: cp "%s" "%t/test.cpp"
+// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs
+// RUN: llvm-bcanalyzer %t/docs/bc/docs.bc --dump | FileCheck %s
+
+/// \brief Brief description.
+///
+/// Extended description that
+/// continues onto the next line.
+/// 
+/// <ul class="test">
+///   <li> Testing.
+/// </ul>
+///
+/// \verbatim
+/// The description continues.
+/// \endverbatim
+///
+/// \param [out] I is a parameter.
+/// \param J is a parameter.
+/// \return void
+void F(int I, int J);
+
+/// Bonus comment on definition
+void F(int I, int J) {}
+
+// CHECK: <BLOCKINFO_BLOCK/>
+// CHECK-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
+  // CHECK-NEXT: <Version abbrevid=4 op0=1/>
+// CHECK-NEXT: </VersionBlock>
+// CHECK-NEXT: <FunctionBlock NumWords=383 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '7574630614A535710E5A6ABCFFF98BCA2D06A4CA'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'F'
+  // CHECK-NEXT: <CommentBlock NumWords=345 BlockCodeSize=4>
+    // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'FullComment'
+    // CHECK-NEXT: <CommentBlock NumWords=13 BlockCodeSize=4>
+      // CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
+      // CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+      // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: <CommentBlock NumWords=31 BlockCodeSize=4>
+      // CHECK-NEXT: <Kind abbrevid=4 op0=19/> blob data = 'BlockCommandComment'
+      // CHECK-NEXT: <Name abbrevid=6 op0=5/> blob data = 'brief'
+      // CHECK-NEXT: <CommentBlock NumWords=19 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
+        // CHECK-NEXT: <CommentBlock NumWords=11 BlockCodeSize=4>
+          // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+          // CHECK-NEXT: <Text abbrevid=5 op0=19/> blob data = ' Brief description.'
+        // CHECK-NEXT: </CommentBlock>
+      // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: <CommentBlock NumWords=37 BlockCodeSize=4>
+      // CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
+      // CHECK-NEXT: <CommentBlock NumWords=13 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+        // CHECK-NEXT: <Text abbrevid=5 op0=26/> blob data = ' Extended description that'
+      // CHECK-NEXT: </CommentBlock>
+      // CHECK-NEXT: <CommentBlock NumWords=14 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+        // CHECK-NEXT: <Text abbrevid=5 op0=30/> blob data = ' continues onto the next line.'
+      // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: <CommentBlock NumWords=76 BlockCodeSize=4>
+      // CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
+      // CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+      // CHECK-NEXT: </CommentBlock>
+      // CHECK-NEXT: <CommentBlock NumWords=14 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=19/> blob data = 'HTMLStartTagComment'
+        // CHECK-NEXT: <Name abbrevid=6 op0=2/> blob data = 'ul'
+        // CHECK-NEXT: <AttrKey abbrevid=12 op0=5/> blob data = 'class'
+        // CHECK-NEXT: <AttrVal abbrevid=13 op0=4/> blob data = 'test'
+      // CHECK-NEXT: </CommentBlock>
+      // CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+      // CHECK-NEXT: </CommentBlock>
+      // CHECK-NEXT: <CommentBlock NumWords=9 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=19/> blob data = 'HTMLStartTagComment'
+        // CHECK-NEXT: <Name abbrevid=6 op0=2/> blob data = 'li'
+      // CHECK-NEXT: </CommentBlock>
+      // CHECK-NEXT: <CommentBlock NumWords=9 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+        // CHECK-NEXT: <Text abbrevid=5 op0=9/> blob data = ' Testing.'
+      // CHECK-NEXT: </CommentBlock>
+      // CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+      // CHECK-NEXT: </CommentBlock>
+      // CHECK-NEXT: <CommentBlock NumWords=9 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=17/> blob data = 'HTMLEndTagComment'
+        // CHECK-NEXT: <Name abbrevid=6 op0=2/> blob data = 'ul'
+        // CHECK-NEXT: <SelfClosing abbrevid=10 op0=1/>
+      // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: <CommentBlock NumWords=13 BlockCodeSize=4>
+      // CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
+      // CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+      // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: <CommentBlock NumWords=32 BlockCodeSize=4>
+      // CHECK-NEXT: <Kind abbrevid=4 op0=20/> blob data = 'VerbatimBlockComment'
+      // CHECK-NEXT: <Name abbrevid=6 op0=8/> blob data = 'verbatim'
+      // CHECK-NEXT: <CloseName abbrevid=9 op0=11/> blob data = 'endverbatim'
+      // CHECK-NEXT: <CommentBlock NumWords=16 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=24/> blob data = 'VerbatimBlockLineComment'
+        // CHECK-NEXT: <Text abbrevid=5 op0=27/> blob data = ' The description continues.'
+      // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: <CommentBlock NumWords=13 BlockCodeSize=4>
+      // CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
+      // CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+      // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: <CommentBlock NumWords=39 BlockCodeSize=4>
+      // CHECK-NEXT: <Kind abbrevid=4 op0=19/> blob data = 'ParamCommandComment'
+      // CHECK-NEXT: <Direction abbrevid=7 op0=5/> blob data = '[out]'
+      // CHECK-NEXT: <ParamName abbrevid=8 op0=1/> blob data = 'I'
+      // CHECK-NEXT: <Explicit abbrevid=11 op0=1/>
+      // CHECK-NEXT: <CommentBlock NumWords=25 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
+        // CHECK-NEXT: <CommentBlock NumWords=10 BlockCodeSize=4>
+          // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+          // CHECK-NEXT: <Text abbrevid=5 op0=16/> blob data = ' is a parameter.'
+        // CHECK-NEXT: </CommentBlock>
+        // CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
+          // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+        // CHECK-NEXT: </CommentBlock>
+      // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: <CommentBlock NumWords=38 BlockCodeSize=4>
+      // CHECK-NEXT: <Kind abbrevid=4 op0=19/> blob data = 'ParamCommandComment'
+      // CHECK-NEXT: <Direction abbrevid=7 op0=4/> blob data = '[in]'
+      // CHECK-NEXT: <ParamName abbrevid=8 op0=1/> blob data = 'J'
+      // CHECK-NEXT: <CommentBlock NumWords=25 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
+        // CHECK-NEXT: <CommentBlock NumWords=10 BlockCodeSize=4>
+          // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+          // CHECK-NEXT: <Text abbrevid=5 op0=16/> blob data = ' is a parameter.'
+        // CHECK-NEXT: </CommentBlock>
+        // CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
+          // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+        // CHECK-NEXT: </CommentBlock>
+      // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: <CommentBlock NumWords=28 BlockCodeSize=4>
+      // CHECK-NEXT: <Kind abbrevid=4 op0=19/> blob data = 'BlockCommandComment'
+      // CHECK-NEXT: <Name abbrevid=6 op0=6/> blob data = 'return'
+      // CHECK-NEXT: <CommentBlock NumWords=16 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
+        // CHECK-NEXT: <CommentBlock NumWords=8 BlockCodeSize=4>
+          // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+          // CHECK-NEXT: <Text abbrevid=5 op0=5/> blob data = ' void'
+        // CHECK-NEXT: </CommentBlock>
+      // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: </CommentBlock>
+  // CHECK-NEXT: </CommentBlock>
+  // CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=4/> blob data = 'void'
+  // CHECK-NEXT: </TypeBlock>
+  // CHECK-NEXT: <FieldTypeBlock NumWords=6 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
+    // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'I'
+  // CHECK-NEXT: </FieldTypeBlock>
+  // CHECK-NEXT: <FieldTypeBlock NumWords=6 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
+    // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'J'
+  // CHECK-NEXT: </FieldTypeBlock>
+// CHECK-NEXT: </FunctionBlock>
+// CHECK-NEXT: <FunctionBlock NumWords=66 BlockCodeSize=4>
+  // CHECK-NEXT: <USR abbrevid=4 op0=40/> blob data = '7574630614A535710E5A6ABCFFF98BCA2D06A4CA'
+  // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'F'
+  // CHECK-NEXT: <CommentBlock NumWords=28 BlockCodeSize=4>
+    // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'FullComment'
+    // CHECK-NEXT: <CommentBlock NumWords=21 BlockCodeSize=4>
+      // CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
+      // CHECK-NEXT: <CommentBlock NumWords=13 BlockCodeSize=4>
+        // CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
+        // CHECK-NEXT: <Text abbrevid=5 op0=28/> blob data = ' Bonus comment on definition'
+      // CHECK-NEXT: </CommentBlock>
+    // CHECK-NEXT: </CommentBlock>
+  // CHECK-NEXT: </CommentBlock>
+  // CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=4/> blob data = 'void'
+  // CHECK-NEXT: </TypeBlock>
+  // CHECK-NEXT: <FieldTypeBlock NumWords=6 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
+    // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'I'
+  // CHECK-NEXT: </FieldTypeBlock>
+  // CHECK-NEXT: <FieldTypeBlock NumWords=6 BlockCodeSize=4>
+    // CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
+    // CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'J'
+  // CHECK-NEXT: </FieldTypeBlock>
+// CHECK-NEXT: </FunctionBlock>
Index: clang-doc/tool/ClangDocMain.cpp
===================================================================
--- clang-doc/tool/ClangDocMain.cpp
+++ clang-doc/tool/ClangDocMain.cpp
@@ -19,7 +19,9 @@
 //===----------------------------------------------------------------------===//
 
 #include <string>
+#include "BitcodeWriter.h"
 #include "ClangDoc.h"
+#include "Reducer.h"
 #include "clang/AST/AST.h"
 #include "clang/AST/Decl.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -52,6 +54,10 @@
     "dump-mapper", llvm::cl::desc("Dump mapper results to bitcode file."),
     llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
 
+static llvm::cl::opt<bool> DumpResult(
+    "dump", llvm::cl::desc("Dump intermediate results to bitcode file."),
+    llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
+
 static llvm::cl::opt<bool> OmitFilenames(
     "omit-filenames", llvm::cl::desc("Omit filenames in output."),
     llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
@@ -112,5 +118,39 @@
     });
   }
 
+  // Reducing phase
+  llvm::outs() << "Reducing infos...\n";
+  auto Infos = doc::mergeInfos(Exec->get()->getToolResults());
+  if (!Infos) {
+    llvm::errs() << "Error reducing infos.\n";
+    return 1;
+  }
+
+  if (DumpResult) {
+    llvm::outs() << "Writing intermediate results...\n";
+    SmallString<2048> Buffer;
+    llvm::BitstreamWriter Stream(Buffer);
+    doc::ClangDocBitcodeWriter Writer(Stream, OmitFilenames);
+    Writer.emitInfoSet(Infos);
+    SmallString<128> IRRootPath;
+    llvm::sys::path::native(OutDirectory, IRRootPath);
+    llvm::sys::path::append(IRRootPath, "bc");
+    std::error_code DirectoryStatus =
+        llvm::sys::fs::create_directories(IRRootPath);
+    if (DirectoryStatus != OK) {
+      llvm::errs() << "Unable to create documentation directories.\n";
+      return 1;
+    }
+    llvm::sys::path::append(IRRootPath, "docs.bc");
+    std::error_code OutErrorInfo;
+    llvm::raw_fd_ostream OS(IRRootPath, OutErrorInfo, llvm::sys::fs::F_None);
+    if (OutErrorInfo != OK) {
+      llvm::errs() << "Error opening documentation file.\n";
+      return 1;
+    }
+    OS << Buffer;
+    OS.close();
+  }
+
   return 0;
 }
Index: clang-doc/Representation.h
===================================================================
--- clang-doc/Representation.h
+++ clang-doc/Representation.h
@@ -34,10 +34,17 @@
   IT_default
 };
 
+enum class TypeInfoType {
+  TT_type,
+  TT_field_type,
+  TT_member_type,
+};
+
 // A representation of a parsed comment.
 struct CommentInfo {
   CommentInfo() = default;
   CommentInfo(CommentInfo &&Other) : Children(std::move(Other.Children)) {}
+
   SmallString<16> Kind;
   SmallString<64> Text;
   SmallString<16> Name;
@@ -58,6 +65,7 @@
 
   SmallString<16> USR;
   InfoType RefType = InfoType::IT_default;
+  Info *Ref;
 };
 
 // TODO: Pull the CommentInfo for a type out of the info's CommentInfo.
@@ -101,13 +109,26 @@
 /// A base struct for Infos.
 struct Info {
   Info() = default;
-  Info(Info &&Other) : Description(std::move(Other.Description)) {}
-  virtual ~Info() = default;
+  Info(Info &&Other)
+      : USR(Other.USR),
+        Name(Other.Name),
+        Namespace(std::move(Other.Namespace)),
+        Description(std::move(Other.Description)),
+        Namespaces(std::move(Other.Namespaces)),
+        Records(std::move(Other.Records)),
+        Functions(std::move(Other.Functions)),
+        Enums(std::move(Other.Enums)) {}
 
   SmallString<16> USR;
   SmallString<16> Name;
   llvm::SmallVector<Reference, 4> Namespace;
   std::vector<CommentInfo> Description;
+
+  // References to be populated after reducer phase.
+  llvm::SmallVector<Reference, 4> Namespaces;
+  llvm::SmallVector<Reference, 4> Records;
+  llvm::SmallVector<Reference, 4> Functions;
+  llvm::SmallVector<Reference, 4> Enums;
 };
 
 struct NamespaceInfo : public Info {};
@@ -146,6 +167,47 @@
 
 // TODO: Add functionality to include separate markdown pages.
 
+class InfoSet {
+ public:
+  InfoSet() = default;
+
+  void insert(StringRef Key, NamespaceInfo &&I);
+  void insert(StringRef Key, RecordInfo &&I);
+  void insert(StringRef Key, EnumInfo &&I);
+  void insert(StringRef Key, FunctionInfo &&I);
+
+  /// Returns the info with a Key, if it exists. Valid until next insert().
+  template <typename T>
+  T *find(StringRef Key);
+
+  /// Populate the type references in all infos
+  void createReferences();
+
+  std::vector<NamespaceInfo> &getNamespaceInfos() { return NamespaceInfos; }
+  std::vector<FunctionInfo> &getFunctionInfos() { return FunctionInfos; }
+  std::vector<RecordInfo> &getRecordInfos() { return RecordInfos; }
+  std::vector<EnumInfo> &getEnumInfos() { return EnumInfos; }
+
+ private:
+  void resolveReferences(llvm::SmallVectorImpl<Reference> &References,
+                         Reference &Caller);
+  void resolveReferences(llvm::SmallVectorImpl<TypeInfo> &References,
+                         Reference &Caller);
+  void resolveReferences(llvm::SmallVectorImpl<FieldTypeInfo> &References,
+                         Reference &Caller);
+  void resolveReferences(llvm::SmallVectorImpl<MemberTypeInfo> &References,
+                         Reference &Caller);
+  void resolveReferences(TypeInfo &TI, Reference &Caller);
+  void resolveReferences(Reference &Ref, Reference &Caller);
+  void addCaller(Info *I, Reference &Caller);
+
+  std::vector<NamespaceInfo> NamespaceInfos;
+  std::vector<FunctionInfo> FunctionInfos;
+  std::vector<RecordInfo> RecordInfos;
+  std::vector<EnumInfo> EnumInfos;
+  llvm::DenseMap<StringRef, size_t> InfoIndex;
+};
+
 }  // namespace doc
 }  // namespace clang
 
Index: clang-doc/Representation.cpp
===================================================================
--- /dev/null
+++ clang-doc/Representation.cpp
@@ -0,0 +1,205 @@
+///===-- ClangDocRepresentation.cpp - ClangDocRepresenation -----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Representation.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+static void mergeInfoBase(Info &L, Info &R) {
+  assert(L.USR == R.USR);
+  assert(L.Name == R.Name);
+  if (L.Namespace.empty()) std::move(R.Namespace);
+  std::move(R.Description.begin(), R.Description.end(),
+            std::back_inserter(L.Description));
+}
+
+static void mergeSymbolInfoBase(SymbolInfo &L, SymbolInfo &R) {
+  mergeInfoBase(L, R);
+  if (R.DefLoc)
+    L.DefLoc = std::move(R.DefLoc);
+  L.Loc.insert(L.Loc.end(), R.Loc.begin(), R.Loc.end());
+}
+
+static void mergeInfo(NamespaceInfo &L, NamespaceInfo &R) {
+  mergeInfoBase(L, R);
+}
+
+static void mergeInfo(RecordInfo &L, RecordInfo &R) {
+  mergeSymbolInfoBase(L, R);
+  assert(L.TagType == R.TagType);
+  if (L.Members.empty()) L.Members = std::move(R.Members);
+  if (L.Parents.empty()) L.Parents = std::move(R.Parents);
+  if (L.VirtualParents.empty()) L.VirtualParents = std::move(R.VirtualParents);
+}
+
+static void mergeInfo(EnumInfo &L, EnumInfo &R) {
+  mergeSymbolInfoBase(L, R);
+  assert(L.Scoped == R.Scoped);
+  if (L.Members.empty()) L.Members = std::move(R.Members);
+}
+
+static void mergeInfo(FunctionInfo &L, FunctionInfo &R) {
+  mergeSymbolInfoBase(L, R);
+  assert(L.ReturnType.Type.USR == R.ReturnType.Type.USR);
+  assert(L.Access == R.Access);
+  if (L.Parent.USR.empty()) {
+    L.Parent.USR = R.Parent.USR;
+    L.Parent.RefType = R.Parent.RefType;
+  }
+  if (L.Params.empty()) L.Params = std::move(R.Params);
+}
+
+#define FIND_FUNC(X)                          \
+  template <>                                 \
+  X *InfoSet::find(StringRef Key) {           \
+    auto I = InfoIndex.find(Key);             \
+    if (I == InfoIndex.end()) return nullptr; \
+    return &X##s[I->second];                  \
+  }
+
+FIND_FUNC(NamespaceInfo)
+FIND_FUNC(RecordInfo)
+FIND_FUNC(EnumInfo)
+FIND_FUNC(FunctionInfo)
+
+#undef FIND_FUNC
+
+#define INSERT_FUNC(X)                                \
+  void InfoSet::insert(StringRef Key, X &&I) {        \
+    auto R = InfoIndex.try_emplace(Key, X##s.size()); \
+    if (R.second)                                     \
+      X##s.push_back(std::move(I));                   \
+    else                                              \
+      mergeInfo(X##s[R.first->second], I);            \
+  }
+
+INSERT_FUNC(NamespaceInfo)
+INSERT_FUNC(RecordInfo)
+INSERT_FUNC(EnumInfo)
+INSERT_FUNC(FunctionInfo)
+
+#undef INSERT_FUNC
+
+void InfoSet::createReferences() {
+  Reference Caller;
+  for (auto &I : NamespaceInfos) {
+    Caller.USR = I.USR;
+    Caller.RefType = InfoType::IT_namespace;
+    Caller.Ref = &I;
+    resolveReferences(I.Namespace, Caller);
+  }
+  for (auto &I : FunctionInfos) {
+    Caller.USR = I.USR;
+    Caller.RefType = InfoType::IT_function;
+    Caller.Ref = &I;
+    resolveReferences(I.Namespace, Caller);
+    resolveReferences(I.Parent, Caller);
+    resolveReferences(I.ReturnType, Caller);
+    resolveReferences(I.Params, Caller);
+  }
+  for (auto &I : RecordInfos) {
+    Caller.USR = I.USR;
+    Caller.RefType = InfoType::IT_record;
+    Caller.Ref = &I;
+    resolveReferences(I.Namespace, Caller);
+    resolveReferences(I.Members, Caller);
+    resolveReferences(I.Parents, Caller);
+    resolveReferences(I.VirtualParents, Caller);
+  }
+  for (auto &I : EnumInfos) {
+    Caller.USR = I.USR;
+    Caller.RefType = InfoType::IT_enum;
+    Caller.Ref = &I;
+    resolveReferences(I.Namespace, Caller);
+    resolveReferences(I.Members, Caller);
+  }
+}
+
+void InfoSet::resolveReferences(llvm::SmallVectorImpl<Reference> &References,
+                                Reference &Caller) {
+  for (auto &Ref : References) resolveReferences(Ref, Caller);
+}
+
+void InfoSet::resolveReferences(llvm::SmallVectorImpl<TypeInfo> &References,
+                                Reference &Caller) {
+  for (auto &Ref : References) resolveReferences(Ref.Type, Caller);
+}
+
+void InfoSet::resolveReferences(llvm::SmallVectorImpl<FieldTypeInfo> &References,
+                                Reference &Caller) {
+  for (auto &Ref : References) resolveReferences(Ref.Type, Caller);
+}
+
+void InfoSet::resolveReferences(
+    llvm::SmallVectorImpl<MemberTypeInfo> &References, Reference &Caller) {
+  for (auto &Ref : References) resolveReferences(Ref.Type, Caller);
+}
+
+void InfoSet::resolveReferences(TypeInfo &TI, Reference &Caller) {
+  resolveReferences(TI.Type, Caller);
+}
+
+void InfoSet::resolveReferences(Reference &Ref, Reference &Caller) {
+  switch (Ref.RefType) {
+    case InfoType::IT_namespace:
+      if (auto *I = find<NamespaceInfo>(Ref.USR)) {
+        Ref.Ref = I;
+        addCaller(I, Caller);
+      }
+      return;
+    case InfoType::IT_record:
+      if (auto *I = find<RecordInfo>(Ref.USR)) {
+        Ref.Ref = I;
+        addCaller(I, Caller);
+      }
+      return;
+    case InfoType::IT_function:
+      if (auto *I = find<FunctionInfo>(Ref.USR)) {
+        Ref.Ref = I;
+        addCaller(I, Caller);
+      }
+      return;
+    case InfoType::IT_enum:
+      if (auto *I = find<EnumInfo>(Ref.USR)) {
+        Ref.Ref = I;
+        addCaller(I, Caller);
+      }
+      return;
+    case InfoType::IT_default:
+      // We don't resolve non-USR references.
+      return;
+  }
+}
+
+void InfoSet::addCaller(Info *I, Reference &Caller) {
+  switch (Caller.RefType) {
+    case InfoType::IT_namespace:
+      I->Namespaces.push_back(Caller);
+      return;
+    case InfoType::IT_record:
+      I->Records.push_back(Caller);
+      return;
+    case InfoType::IT_function:
+      I->Functions.push_back(Caller);
+      return;
+    case InfoType::IT_enum:
+      I->Enums.push_back(Caller);
+      return;
+    default:
+      // We don't resolve non-USR references.
+      return;
+  }
+}
+
+}  // namespace doc
+}  // namespace clang
Index: clang-doc/Reducer.h
===================================================================
--- /dev/null
+++ clang-doc/Reducer.h
@@ -0,0 +1,26 @@
+///===-- ClangDocReducer.h - ClangDocReducer -------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_REDUCER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_REDUCER_H
+
+#include "Representation.h"
+#include "clang/Tooling/Execution.h"
+#include "clang/Tooling/Tooling.h"
+
+namespace clang {
+namespace doc {
+
+// Combine occurrences of the same info across translation units.
+std::unique_ptr<InfoSet> mergeInfos(tooling::ToolResults *Results);
+
+}  // namespace doc
+}  // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_REDUCER_H
Index: clang-doc/Reducer.cpp
===================================================================
--- /dev/null
+++ clang-doc/Reducer.cpp
@@ -0,0 +1,34 @@
+///===-- ClangDocReducer.h - ClangDocReducer -------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Reducer.h"
+#include "BitcodeReader.h"
+#include "Representation.h"
+
+namespace clang {
+namespace doc {
+
+std::unique_ptr<InfoSet> mergeInfos(tooling::ToolResults *Results) {
+  std::unique_ptr<InfoSet> UniqueInfos = llvm::make_unique<InfoSet>();
+  bool Err = false;
+  Results->forEachResult([&](StringRef Key, StringRef Value) {
+    llvm::BitstreamCursor Stream(Value);
+    ClangDocBitcodeReader Reader(Stream);
+    if (!Reader.readBitstream(UniqueInfos)) {
+      Err = true;
+      return;
+    }
+  });
+  if (Err) return nullptr;
+  UniqueInfos->createReferences();
+  return UniqueInfos;
+}
+
+}  // namespace doc
+}  // namespace clang
Index: clang-doc/CMakeLists.txt
===================================================================
--- clang-doc/CMakeLists.txt
+++ clang-doc/CMakeLists.txt
@@ -3,9 +3,12 @@
   )
 
 add_clang_library(clangDoc
+  BitcodeReader.cpp
   BitcodeWriter.cpp
   Mapper.cpp
   Serialize.cpp
+  Reducer.cpp
+  Representation.cpp
 
   LINK_LIBS
   clangAnalysis
Index: clang-doc/BitcodeWriter.h
===================================================================
--- clang-doc/BitcodeWriter.h
+++ clang-doc/BitcodeWriter.h
@@ -64,7 +64,9 @@
 
 // New Ids need to be added to the enum here, and to the relevant IdNameMap and
 // initialization list in the implementation file.
-#define INFORECORDS(X) X##_USR, X##_NAME, X##_NAMESPACE
+#define INFORECORDS(X)                                           \
+  X##_USR, X##_NAME, X##_NAMESPACE, X##_NAMESPACES, X##_RECORDS, \
+      X##_FUNCTIONS, X##_ENUMS
 
 enum RecordId {
   VERSION = 1,
@@ -175,6 +177,7 @@
 
   template <typename T>
   void emitBlock(const T &I);
+  void emitInfoSet(std::unique_ptr<InfoSet> &ISet);
 
  private:
   class AbbreviationMap {
Index: clang-doc/BitcodeWriter.cpp
===================================================================
--- clang-doc/BitcodeWriter.cpp
+++ clang-doc/BitcodeWriter.cpp
@@ -149,34 +149,50 @@
           {NAMESPACE_USR, {"USR", &StringAbbrev}},
           {NAMESPACE_NAME, {"Name", &StringAbbrev}},
           {NAMESPACE_NAMESPACE, {"Namespace", &ReferenceAbbrev}},
+          {NAMESPACE_NAMESPACES, {"Namespaces", &ReferenceAbbrev}},
+          {NAMESPACE_RECORDS, {"Records", &ReferenceAbbrev}},
+          {NAMESPACE_FUNCTIONS, {"Functions", &ReferenceAbbrev}},
+          {NAMESPACE_ENUMS, {"Enums", &ReferenceAbbrev}},
           {ENUM_USR, {"USR", &StringAbbrev}},
           {ENUM_NAME, {"Name", &StringAbbrev}},
           {ENUM_NAMESPACE, {"Namespace", &ReferenceAbbrev}},
           {ENUM_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
           {ENUM_LOCATION, {"Location", &LocationAbbrev}},
           {ENUM_SCOPED, {"Scoped", &BoolAbbrev}},
+          {ENUM_NAMESPACES, {"Namespaces", &ReferenceAbbrev}},
+          {ENUM_RECORDS, {"Records", &ReferenceAbbrev}},
+          {ENUM_FUNCTIONS, {"Functions", &ReferenceAbbrev}},
+          {ENUM_ENUMS, {"Enums", &ReferenceAbbrev}},
           {RECORD_USR, {"USR", &StringAbbrev}},
           {RECORD_NAME, {"Name", &StringAbbrev}},
           {RECORD_NAMESPACE, {"Namespace", &ReferenceAbbrev}},
           {RECORD_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
           {RECORD_LOCATION, {"Location", &LocationAbbrev}},
           {RECORD_TAG_TYPE, {"TagType", &IntAbbrev}},
           {RECORD_PARENT, {"Parent", &ReferenceAbbrev}},
           {RECORD_VPARENT, {"VParent", &ReferenceAbbrev}},
+          {RECORD_NAMESPACES, {"Namespaces", &ReferenceAbbrev}},
+          {RECORD_RECORDS, {"Records", &ReferenceAbbrev}},
+          {RECORD_FUNCTIONS, {"Functions", &ReferenceAbbrev}},
+          {RECORD_ENUMS, {"Enums", &ReferenceAbbrev}},
           {FUNCTION_USR, {"USR", &StringAbbrev}},
           {FUNCTION_NAME, {"Name", &StringAbbrev}},
           {FUNCTION_NAMESPACE, {"Namespace", &ReferenceAbbrev}},
           {FUNCTION_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
           {FUNCTION_LOCATION, {"Location", &LocationAbbrev}},
           {FUNCTION_PARENT, {"Parent", &ReferenceAbbrev}},
           {FUNCTION_ACCESS, {"Access", &IntAbbrev}},
-          {FUNCTION_IS_METHOD, {"IsMethod", &BoolAbbrev}}};
-      // assert(Inits.size() == RecordIdCount);
+          {FUNCTION_IS_METHOD, {"IsMethod", &BoolAbbrev}},
+          {FUNCTION_NAMESPACES, {"Namespaces", &ReferenceAbbrev}},
+          {FUNCTION_RECORDS, {"Records", &ReferenceAbbrev}},
+          {FUNCTION_FUNCTIONS, {"Functions", &ReferenceAbbrev}},
+          {FUNCTION_ENUMS, {"Enums", &ReferenceAbbrev}}};
+      assert(Inits.size() == RecordIdCount);
       for (const auto &Init : Inits) {
         RecordIdNameMap[Init.first] = Init.second;
         assert((Init.second.Name.size() + 1) <= BitCodeConstants::RecordSize);
       }
-      // assert(RecordIdNameMap.size() == RecordIdCount);
+      assert(RecordIdNameMap.size() == RecordIdCount);
       return RecordIdNameMap;
     }();
 
@@ -200,19 +216,24 @@
         // Enum Block
         {BI_ENUM_BLOCK_ID,
          {ENUM_USR, ENUM_NAME, ENUM_NAMESPACE, ENUM_DEFLOCATION, ENUM_LOCATION,
-          ENUM_SCOPED}},
+          ENUM_SCOPED, ENUM_NAMESPACES, ENUM_RECORDS, ENUM_FUNCTIONS,
+          ENUM_ENUMS}},
         // Namespace Block
         {BI_NAMESPACE_BLOCK_ID,
-         {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_NAMESPACE}},
+         {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_NAMESPACE,
+          NAMESPACE_NAMESPACES, NAMESPACE_RECORDS, NAMESPACE_FUNCTIONS,
+          NAMESPACE_ENUMS}},
         // Record Block
         {BI_RECORD_BLOCK_ID,
          {RECORD_USR, RECORD_NAME, RECORD_NAMESPACE, RECORD_DEFLOCATION,
-          RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_PARENT, RECORD_VPARENT}},
+          RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_PARENT, RECORD_VPARENT,
+          RECORD_NAMESPACES, RECORD_RECORDS, RECORD_FUNCTIONS, RECORD_ENUMS}},
         // Function Block
         {BI_FUNCTION_BLOCK_ID,
          {FUNCTION_USR, FUNCTION_NAME, FUNCTION_NAMESPACE, FUNCTION_DEFLOCATION,
           FUNCTION_LOCATION, FUNCTION_PARENT, FUNCTION_ACCESS,
-          FUNCTION_IS_METHOD}}};
+          FUNCTION_IS_METHOD, FUNCTION_NAMESPACES, FUNCTION_RECORDS,
+          FUNCTION_FUNCTIONS, FUNCTION_ENUMS}}};
 
 // AbbreviationMap
 
@@ -403,11 +424,15 @@
   for (const auto &C : I.Children) emitBlock(*C);
 }
 
-#define EMITINFO(X)                                               \
-  emitRecord(I.USR, X##_USR);                                     \
-  emitRecord(I.Name, X##_NAME);                                   \
-  for (const auto &N : I.Namespace) emitRecord(N, X##_NAMESPACE); \
-  for (const auto &CI : I.Description) emitBlock(CI);
+#define EMITINFO(X)                                                 \
+  emitRecord(I.USR, X##_USR);                                       \
+  emitRecord(I.Name, X##_NAME);                                     \
+  for (const auto &N : I.Namespace) emitRecord(N, X##_NAMESPACE);   \
+  for (const auto &CI : I.Description) emitBlock(CI);               \
+  for (const auto &R : I.Namespaces) emitRecord(R, X##_NAMESPACES); \
+  for (const auto &R : I.Records) emitRecord(R, X##_RECORDS);       \
+  for (const auto &R : I.Functions) emitRecord(R, X##_FUNCTIONS);  \
+  for (const auto &R : I.Enums) emitRecord(R, X##_ENUMS);
 
 void ClangDocBitcodeWriter::emitBlockContent(const NamespaceInfo &I) {
   EMITINFO(NAMESPACE)
@@ -443,5 +468,12 @@
 
 #undef EMITINFO
 
+void ClangDocBitcodeWriter::emitInfoSet(std::unique_ptr<InfoSet> &ISet) {
+  for (const auto &I : ISet->getNamespaceInfos()) emitBlock(I);
+  for (const auto &I : ISet->getFunctionInfos()) emitBlock(I);
+  for (const auto &I : ISet->getRecordInfos()) emitBlock(I);
+  for (const auto &I : ISet->getEnumInfos()) emitBlock(I);
+}
+
 }  // namespace doc
 }  // namespace clang
Index: clang-doc/BitcodeReader.h
===================================================================
--- /dev/null
+++ clang-doc/BitcodeReader.h
@@ -0,0 +1,99 @@
+//===--  BitcodeReader.h - ClangDoc Bitcode Reader --------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a reader for parsing the clang-doc internal
+// representation to LLVM bitcode. The reader takes in a stream of bits and
+// generates the set of infos that it represents.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEREADER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEREADER_H
+
+#include "BitcodeWriter.h"
+#include "Representation.h"
+#include "clang/AST/AST.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Bitcode/BitstreamReader.h"
+
+namespace clang {
+namespace doc {
+
+// Class to read bitstream into an InfoSet collection
+class ClangDocBitcodeReader {
+ public:
+  ClangDocBitcodeReader(llvm::BitstreamCursor &Stream) : Stream(Stream) {}
+
+  bool readBitstream(std::unique_ptr<InfoSet> &IS);
+
+ private:
+  enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin };
+
+  // Top level parsing
+  bool validateStream();
+  bool readVersion();
+  bool readBlockInfoBlock();
+
+  template <typename TInfo>
+  bool readBlockToInfo(unsigned ID, std::unique_ptr<InfoSet> &IS, TInfo &&I);
+
+  // Reading records
+  template <typename TInfo>
+  bool readRecord(unsigned ID, TInfo &I);
+
+  bool parseRecord(unsigned ID, llvm::StringRef Blob, unsigned VersionNo);
+  bool parseRecord(unsigned ID, llvm::StringRef Blob, NamespaceInfo &I);
+  bool parseRecord(unsigned ID, llvm::StringRef Blob, RecordInfo &I);
+  bool parseRecord(unsigned ID, llvm::StringRef Blob, EnumInfo &I);
+  bool parseRecord(unsigned ID, llvm::StringRef Blob, FunctionInfo &I);
+  bool parseRecord(unsigned ID, llvm::StringRef Blob, TypeInfo &I);
+  bool parseRecord(unsigned ID, llvm::StringRef Blob, FieldTypeInfo &I);
+  bool parseRecord(unsigned ID, llvm::StringRef Blob, MemberTypeInfo &I);
+  bool parseRecord(unsigned ID, llvm::StringRef Blob, CommentInfo &I);
+
+  template <unsigned U>
+  void storeData(llvm::SmallString<U> &Field, llvm::StringRef Blob);
+
+  void storeData(bool &Field, llvm::StringRef Blob);
+  void storeData(int &Field, llvm::StringRef Blob);
+  void storeData(AccessSpecifier &Field, llvm::StringRef Blob);
+  void storeData(TagTypeKind &Field, llvm::StringRef Blob);
+  void storeData(llvm::Optional<Location> &Field, llvm::StringRef Blob);
+  void storeData(Reference &Field, llvm::StringRef Blob);
+  void storeData(llvm::SmallVectorImpl<Location> &Field, llvm::StringRef Blob);
+  void storeData(llvm::SmallVectorImpl<Reference> &Field, llvm::StringRef Blob);
+  void storeData(llvm::SmallVectorImpl<llvm::SmallString<16>> &Field,
+                 llvm::StringRef Blob);
+
+  // Reading blocks
+  template <typename TInfo>
+  bool readBlock(unsigned ID, TInfo &I);
+  template <typename TInfo>
+  bool readSubBlock(unsigned ID, TInfo &I);
+  Cursor skipUntilRecordOrBlock(unsigned &BlockOrRecordID);
+
+  // Helpers
+  CommentInfo &getCommentInfo(const unsigned int I);
+  CommentInfo &getCommentInfo(TypeInfo &I);
+  CommentInfo &getCommentInfo(Info &I);
+  CommentInfo &getCommentInfo(CommentInfo &I);
+  CommentInfo &getCommentInfo(std::unique_ptr<CommentInfo> &I);
+
+  template <typename TInfo, typename TTypeInfo>
+  void addTypeInfo(TInfo &I, TTypeInfo &&T);
+
+  llvm::BitstreamCursor &Stream;
+  llvm::SmallVector<uint64_t, 1024> Record;
+  Optional<llvm::BitstreamBlockInfo> BlockInfo;
+};
+
+}  // namespace doc
+}  // namespace clang
+
+#endif  // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEREADER_H
Index: clang-doc/BitcodeReader.cpp
===================================================================
--- /dev/null
+++ clang-doc/BitcodeReader.cpp
@@ -0,0 +1,498 @@
+//===--  BitcodeReader.cpp - ClangDoc Bitcode Reader ------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BitcodeReader.h"
+#include "llvm/ADT/IndexedMap.h"
+#include "llvm/ADT/Optional.h"
+
+namespace clang {
+namespace doc {
+
+// Store data
+template <unsigned U>
+void ClangDocBitcodeReader::storeData(llvm::SmallString<U> &Field,
+                                      llvm::StringRef Blob) {
+  Field = Blob;
+}
+
+void ClangDocBitcodeReader::storeData(bool &Field, llvm::StringRef Blob) {
+  Field = (bool)Record[0];
+}
+
+void ClangDocBitcodeReader::storeData(int &Field, llvm::StringRef Blob) {
+  Field = (int)Record[0];
+}
+
+void ClangDocBitcodeReader::storeData(AccessSpecifier &Field,
+                                      llvm::StringRef Blob) {
+  Field = (AccessSpecifier)Record[0];
+}
+
+void ClangDocBitcodeReader::storeData(TagTypeKind &Field,
+                                      llvm::StringRef Blob) {
+  Field = (TagTypeKind)Record[0];
+}
+
+void ClangDocBitcodeReader::storeData(llvm::Optional<Location> &Field,
+                                      llvm::StringRef Blob) {
+  Field.emplace((int)Record[0], Blob);
+}
+
+void ClangDocBitcodeReader::storeData(Reference &Field, llvm::StringRef Blob) {
+  Field = Reference{Blob, (InfoType)Record[0]};
+}
+
+void ClangDocBitcodeReader::storeData(
+    llvm::SmallVectorImpl<llvm::SmallString<16>> &Field, llvm::StringRef Blob) {
+  Field.push_back(Blob);
+}
+
+void ClangDocBitcodeReader::storeData(llvm::SmallVectorImpl<Location> &Field,
+                                      llvm::StringRef Blob) {
+  Field.emplace_back((int)Record[0], Blob);
+}
+
+void ClangDocBitcodeReader::storeData(llvm::SmallVectorImpl<Reference> &Field,
+                                      llvm::StringRef Blob) {
+  Field.emplace_back(Blob, (InfoType)Record[0]);
+}
+
+// Read records
+
+template <typename TInfo>
+bool ClangDocBitcodeReader::readRecord(unsigned ID, TInfo &I) {
+  Record.clear();
+  llvm::StringRef Blob;
+  unsigned RecID = Stream.readRecord(ID, Record, &Blob);
+  return parseRecord(RecID, Blob, I);
+  return false;
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob,
+                                        unsigned VersionNo) {
+  if (ID == VERSION && Record[0] == VersionNo) return true;
+  return false;
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob,
+                                        NamespaceInfo &I) {
+  switch (ID) {
+    case NAMESPACE_USR:
+      storeData(I.USR, Blob);
+      return true;
+    case NAMESPACE_NAME:
+      storeData(I.Name, Blob);
+      return true;
+    case NAMESPACE_NAMESPACE:
+      storeData(I.Namespace, Blob);
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob,
+                                        RecordInfo &I) {
+  switch (ID) {
+    case RECORD_USR:
+      storeData(I.USR, Blob);
+      return true;
+    case RECORD_NAME:
+      storeData(I.Name, Blob);
+      return true;
+    case RECORD_NAMESPACE:
+      storeData(I.Namespace, Blob);
+      return true;
+    case RECORD_PARENT:
+      storeData(I.Parents, Blob);
+      return true;
+    case RECORD_VPARENT:
+      storeData(I.VirtualParents, Blob);
+      return true;
+    case RECORD_DEFLOCATION:
+      storeData(I.DefLoc, Blob);
+      return true;
+    case RECORD_LOCATION:
+      storeData(I.Loc, Blob);
+      return true;
+    case RECORD_TAG_TYPE:
+      storeData(I.TagType, Blob);
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob,
+                                        EnumInfo &I) {
+  switch (ID) {
+    case ENUM_USR:
+      storeData(I.USR, Blob);
+      return true;
+    case ENUM_NAME:
+      storeData(I.Name, Blob);
+      return true;
+    case ENUM_NAMESPACE:
+      storeData(I.Namespace, Blob);
+      return true;
+    case ENUM_DEFLOCATION:
+      storeData(I.DefLoc, Blob);
+      return true;
+    case ENUM_LOCATION:
+      storeData(I.Loc, Blob);
+      return true;
+    case ENUM_SCOPED:
+      storeData(I.Scoped, Blob);
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob,
+                                        FunctionInfo &I) {
+  switch (ID) {
+    case FUNCTION_USR:
+      storeData(I.USR, Blob);
+      return true;
+    case FUNCTION_NAME:
+      storeData(I.Name, Blob);
+      return true;
+    case FUNCTION_NAMESPACE:
+      storeData(I.Namespace, Blob);
+      return true;
+    case FUNCTION_PARENT:
+      storeData(I.Parent, Blob);
+      return true;
+    case FUNCTION_DEFLOCATION:
+      storeData(I.DefLoc, Blob);
+      return true;
+    case FUNCTION_LOCATION:
+      storeData(I.Loc, Blob);
+      return true;
+    case FUNCTION_ACCESS:
+      storeData(I.Access, Blob);
+      return true;
+    case FUNCTION_IS_METHOD:
+      storeData(I.IsMethod, Blob);
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob,
+                                        TypeInfo &I) {
+  switch (ID) {
+    case TYPE_REF:
+      storeData(I.Type, Blob);
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob,
+                                        FieldTypeInfo &I) {
+  switch (ID) {
+    case FIELD_TYPE_REF:
+      storeData(I.Type, Blob);
+      return true;
+    case FIELD_TYPE_NAME:
+      storeData(I.Name, Blob);
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob,
+                                        MemberTypeInfo &I) {
+  switch (ID) {
+    case MEMBER_TYPE_REF:
+      storeData(I.Type, Blob);
+      return true;
+    case MEMBER_TYPE_NAME:
+      storeData(I.Name, Blob);
+      return true;
+    case MEMBER_TYPE_ACCESS:
+      storeData(I.Access, Blob);
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob,
+                                        CommentInfo &I) {
+  switch (ID) {
+    case COMMENT_KIND:
+      storeData(I.Kind, Blob);
+      return true;
+    case COMMENT_TEXT:
+      storeData(I.Text, Blob);
+      return true;
+    case COMMENT_NAME:
+      storeData(I.Name, Blob);
+      return true;
+    case COMMENT_DIRECTION:
+      storeData(I.Direction, Blob);
+      return true;
+    case COMMENT_PARAMNAME:
+      storeData(I.ParamName, Blob);
+      return true;
+    case COMMENT_CLOSENAME:
+      storeData(I.CloseName, Blob);
+      return true;
+    case COMMENT_ATTRKEY:
+      storeData(I.AttrKeys, Blob);
+      return true;
+    case COMMENT_ATTRVAL:
+      storeData(I.AttrValues, Blob);
+      return true;
+    case COMMENT_ARG:
+      storeData(I.Args, Blob);
+      return true;
+    case COMMENT_SELFCLOSING:
+      storeData(I.SelfClosing, Blob);
+      return true;
+    case COMMENT_EXPLICIT:
+      storeData(I.Explicit, Blob);
+      return true;
+    default:
+      return false;
+  }
+}
+
+// Helpers
+CommentInfo &ClangDocBitcodeReader::getCommentInfo(const unsigned int I) {
+  llvm::errs() << "Cannot have comment subblock.\n";
+  exit(1);
+}
+
+CommentInfo &ClangDocBitcodeReader::getCommentInfo(TypeInfo &I) {
+  llvm::errs() << "Cannot have comment subblock.\n";
+  exit(1);
+}
+
+CommentInfo &ClangDocBitcodeReader::getCommentInfo(Info &I) {
+  I.Description.emplace_back();
+  return I.Description.back();
+}
+
+CommentInfo &ClangDocBitcodeReader::getCommentInfo(CommentInfo &I) {
+  I.Children.emplace_back(llvm::make_unique<CommentInfo>());
+  return *I.Children.back();
+}
+
+CommentInfo &ClangDocBitcodeReader::getCommentInfo(
+    std::unique_ptr<CommentInfo> &I) {
+  I->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  return *I->Children.back();
+}
+
+template <typename TInfo, typename TTypeInfo>
+void ClangDocBitcodeReader::addTypeInfo(TInfo &I, TTypeInfo &&T) {
+  llvm::errs() << "Invalid type for info.\n";
+  exit(1);
+}
+
+template <>
+void ClangDocBitcodeReader::addTypeInfo(EnumInfo &I, TypeInfo &&T) {
+  I.Members.emplace_back(std::move(T));
+}
+
+template <>
+void ClangDocBitcodeReader::addTypeInfo(RecordInfo &I, MemberTypeInfo &&T) {
+  I.Members.emplace_back(std::move(T));
+}
+
+template <>
+void ClangDocBitcodeReader::addTypeInfo(FunctionInfo &I, TypeInfo &&T) {
+  I.ReturnType = std::move(T);
+}
+
+template <>
+void ClangDocBitcodeReader::addTypeInfo(FunctionInfo &I, FieldTypeInfo &&T) {
+  I.Params.emplace_back(std::move(T));
+}
+
+// Read blocks
+template <typename T>
+bool ClangDocBitcodeReader::readBlock(unsigned ID, T &I) {
+  if (Stream.EnterSubBlock(ID)) return false;
+
+  while (true) {
+    unsigned BlockOrCode = 0;
+    Cursor Res = skipUntilRecordOrBlock(BlockOrCode);
+
+    switch (Res) {
+      case Cursor::BadBlock:
+        return false;
+      case Cursor::BlockEnd:
+        return true;
+      case Cursor::BlockBegin:
+        if (readSubBlock(BlockOrCode, I)) continue;
+        if (!Stream.SkipBlock()) return false;
+        continue;
+      case Cursor::Record:
+        break;
+    }
+    if (!readRecord(BlockOrCode, I)) return false;
+  }
+}
+
+template <typename TInfo>
+bool ClangDocBitcodeReader::readSubBlock(unsigned ID, TInfo &I) {
+  switch (ID) {
+    // Blocks can only have Comment or TypeInfo subblocks
+    case BI_COMMENT_BLOCK_ID:
+      if (readBlock(ID, getCommentInfo(I))) return true;
+      return false;
+    case BI_TYPE_BLOCK_ID: {
+      TypeInfo T;
+      if (readBlock(ID, T)) {
+        addTypeInfo(I, std::move(T));
+        return true;
+      }
+    }
+    case BI_FIELD_TYPE_BLOCK_ID: {
+      FieldTypeInfo T;
+      if (readBlock(ID, T)) {
+        addTypeInfo(I, std::move(T));
+        return true;
+      }
+    }
+    case BI_MEMBER_TYPE_BLOCK_ID: {
+      MemberTypeInfo T;
+      if (readBlock(ID, T)) {
+        addTypeInfo(I, std::move(T));
+        return true;
+      }
+      return false;
+    }
+    default:
+      llvm::errs() << "Invalid subblock type.\n";
+      return false;
+  }
+}
+
+ClangDocBitcodeReader::Cursor ClangDocBitcodeReader::skipUntilRecordOrBlock(
+    unsigned &BlockOrRecordID) {
+  BlockOrRecordID = 0;
+
+  while (!Stream.AtEndOfStream()) {
+    unsigned Code = Stream.ReadCode();
+
+    switch ((llvm::bitc::FixedAbbrevIDs)Code) {
+      case llvm::bitc::ENTER_SUBBLOCK:
+        BlockOrRecordID = Stream.ReadSubBlockID();
+        return Cursor::BlockBegin;
+      case llvm::bitc::END_BLOCK:
+        if (Stream.ReadBlockEnd()) return Cursor::BadBlock;
+        return Cursor::BlockEnd;
+      case llvm::bitc::DEFINE_ABBREV:
+        Stream.ReadAbbrevRecord();
+        continue;
+      case llvm::bitc::UNABBREV_RECORD:
+        return Cursor::BadBlock;
+      default:
+        BlockOrRecordID = Code;
+        return Cursor::Record;
+    }
+  }
+  llvm_unreachable("Premature stream end.");
+}
+
+// Validation and overview
+
+bool ClangDocBitcodeReader::validateStream() {
+  if (Stream.AtEndOfStream()) return false;
+
+  // Sniff for the signature.
+  if (Stream.Read(8) != 'D' || Stream.Read(8) != 'O' || Stream.Read(8) != 'C' ||
+      Stream.Read(8) != 'S')
+    return false;
+  return true;
+}
+
+bool ClangDocBitcodeReader::readBlockInfoBlock() {
+  BlockInfo = Stream.ReadBlockInfoBlock();
+  if (!BlockInfo) return false;
+  Stream.setBlockInfo(&*BlockInfo);
+  return true;
+}
+
+// Entry point
+
+bool ClangDocBitcodeReader::readBitstream(std::unique_ptr<InfoSet> &IS) {
+  if (!validateStream()) return false;
+
+  // Read the top level blocks.
+  while (!Stream.AtEndOfStream()) {
+    unsigned Code = Stream.ReadCode();
+    if (Code != llvm::bitc::ENTER_SUBBLOCK) return false;
+
+    unsigned ID = Stream.ReadSubBlockID();
+    switch (ID) {
+      // NamedType and Comment blocks should not appear at the top level
+      case BI_TYPE_BLOCK_ID:
+      case BI_FIELD_TYPE_BLOCK_ID:
+      case BI_MEMBER_TYPE_BLOCK_ID:
+      case BI_COMMENT_BLOCK_ID:
+        llvm::errs() << "Invalid top level block.\n";
+        return false;
+      case BI_VERSION_BLOCK_ID:
+        if (readBlock(ID, VersionNumber)) continue;
+        return false;
+      case llvm::bitc::BLOCKINFO_BLOCK_ID:
+        if (readBlockInfoBlock()) continue;
+        return false;
+      case BI_NAMESPACE_BLOCK_ID: {
+        NamespaceInfo I;
+        if (!readBlockToInfo(ID, IS, std::move(I))) return false;
+        continue;
+      }
+      case BI_RECORD_BLOCK_ID: {
+        RecordInfo I;
+        if (!readBlockToInfo(ID, IS, std::move(I))) return false;
+        continue;
+      }
+      case BI_ENUM_BLOCK_ID: {
+        EnumInfo I;
+        if (!readBlockToInfo(ID, IS, std::move(I))) return false;
+        continue;
+      }
+      case BI_FUNCTION_BLOCK_ID: {
+        FunctionInfo I;
+        if (!readBlockToInfo(ID, IS, std::move(I))) return false;
+        continue;
+      }
+      default:
+        if (!Stream.SkipBlock()) return false;
+        continue;
+    }
+  }
+  return true;
+}
+
+template <typename TInfo>
+bool ClangDocBitcodeReader::readBlockToInfo(unsigned ID,
+                                            std::unique_ptr<InfoSet> &IS,
+                                            TInfo &&I) {
+  if (!readBlock(ID, I)) {
+    llvm::errs() << "Error reading from block.\n";
+    return false;
+  }
+  IS->insert(I.USR, std::move(I));
+  return true;
+}
+
+}  // namespace doc
+}  // namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to