HTMLRewrite.cpp revision 9402b57a0dca4058fe56d7fd84e97fc496421125
16a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//== HTMLRewrite.cpp - Translate source code into prettified HTML --*- C++ -*-//
26a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//
36a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//                     The LLVM Compiler Infrastructure
46a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//
56a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek// This file is distributed under the University of Illinois Open Source
66a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek// License. See LICENSE.TXT for details.
76a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//
86a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//===----------------------------------------------------------------------===//
96a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//
106a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//  This file defines the HTMLRewriter clas, which is used to translate the
116a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//  text of a source file into prettified HTML.
126a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//
136a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//===----------------------------------------------------------------------===//
146a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
156a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek#include "clang/Rewrite/Rewriter.h"
166a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek#include "clang/Rewrite/HTMLRewrite.h"
173245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner#include "clang/Lex/Preprocessor.h"
186a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek#include "clang/Basic/SourceManager.h"
1957df3b950061c73d13d3116f747e79d7955a216aChris Lattner#include "llvm/ADT/SmallString.h"
206a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek#include "llvm/Support/MemoryBuffer.h"
216a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek#include <sstream>
226a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenekusing namespace clang;
236a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
249402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner
255ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// HighlightRange - Highlight a range in the source code with the specified
265ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// start/end tags.  B/E must be in the same file.  This ensures that
275ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// start/end tags are placed at the start/end of each line if the range is
285ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// multiline.
295ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattnervoid html::HighlightRange(Rewriter &R, SourceLocation B, SourceLocation E,
305ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                          const char *StartTag, const char *EndTag) {
315ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  SourceManager &SM = R.getSourceMgr();
325ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  B = SM.getLogicalLoc(B);
335ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  E = SM.getLogicalLoc(E);
345ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  unsigned FileID = SM.getCanonicalFileID(B);
355ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  assert(SM.getCanonicalFileID(E) == FileID && "B/E not in the same file!");
365ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
375ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  unsigned BOffset = SM.getFullFilePos(B);
385ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  unsigned EOffset = SM.getFullFilePos(E);
395ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
405ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  // Include the whole end token in the range.
415ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  EOffset += Lexer::MeasureTokenLength(E, R.getSourceMgr());
425ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
435ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  HighlightRange(R.getEditBuffer(FileID), BOffset, EOffset,
445ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                 SM.getBufferData(FileID).first, StartTag, EndTag);
455ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner}
465ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
475ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// HighlightRange - This is the same as the above method, but takes
485ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// decomposed file locations.
495ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattnervoid html::HighlightRange(RewriteBuffer &RB, unsigned B, unsigned E,
505ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                          const char *BufferStart,
515ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                          const char *StartTag, const char *EndTag) {
529402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  // Insert the tag at the absolute start/end of the range.
535ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  RB.InsertTextAfter(B, StartTag, strlen(StartTag));
545ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  RB.InsertTextBefore(E, EndTag, strlen(EndTag));
555ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
569402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  // Scan the range to see if there is a \r or \n.  If so, and if the line is
579402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  // not blank, insert tags on that line as well.
589402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  bool HadOpenTag = true;
599402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner
609402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  unsigned LastNonWhiteSpace = B;
619402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  for (unsigned i = B; i != E; ++i) {
629402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    switch (BufferStart[i]) {
639402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\r':
649402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\n':
659402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // Okay, we found a newline in the range.  If we have an open tag, we need
669402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // to insert a close tag at the first non-whitespace before the newline.
679402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      if (HadOpenTag)
689402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner        RB.InsertTextBefore(LastNonWhiteSpace+1, EndTag, strlen(EndTag));
699402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner
709402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // Instead of inserting an open tag immediately after the newline, we
719402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // wait until we see a non-whitespace character.  This prevents us from
729402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // inserting tags around blank lines, and also allows the open tag to
739402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // be put *after* whitespace on a non-blank line.
749402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      HadOpenTag = false;
759402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      break;
769402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\0':
779402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case ' ':
789402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\t':
799402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\f':
809402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\v':
819402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // Ignore whitespace.
829402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      break;
839402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner
849402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    default:
859402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // If there is no tag open, do it now.
869402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      if (!HadOpenTag) {
879402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner        RB.InsertTextAfter(i, StartTag, strlen(StartTag));
889402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner        HadOpenTag = true;
899402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      }
909402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner
919402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // Remember this character.
929402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      LastNonWhiteSpace = i;
939402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      break;
949402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    }
959402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  }
965ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner}
975ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
98fa5be3617294f0e3c341f0ecb6b2076478b1b5acTed Kremenekvoid html::EscapeText(Rewriter& R, unsigned FileID,
99fa5be3617294f0e3c341f0ecb6b2076478b1b5acTed Kremenek                      bool EscapeSpaces, bool ReplaceTabs) {
1006a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
1016a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
1026a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  const char* C = Buf->getBufferStart();
1036a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  const char* FileEnd = Buf->getBufferEnd();
1046a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
1056a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  assert (C <= FileEnd);
1066a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
107735271479ac57c27f744806859efd5b001dea248Chris Lattner  RewriteBuffer &RB = R.getEditBuffer(FileID);
108735271479ac57c27f744806859efd5b001dea248Chris Lattner
1096a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  for (unsigned FilePos = 0; C != FileEnd ; ++C, ++FilePos) {
11049cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek
1116a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek    switch (*C) {
112735271479ac57c27f744806859efd5b001dea248Chris Lattner    default: break;
113735271479ac57c27f744806859efd5b001dea248Chris Lattner
114735271479ac57c27f744806859efd5b001dea248Chris Lattner    case ' ':
115735271479ac57c27f744806859efd5b001dea248Chris Lattner      if (EscapeSpaces)
116735271479ac57c27f744806859efd5b001dea248Chris Lattner        RB.ReplaceText(FilePos, 1, "&nbsp;", 6);
117735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
1186a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
119735271479ac57c27f744806859efd5b001dea248Chris Lattner    case '\t':
120735271479ac57c27f744806859efd5b001dea248Chris Lattner      if (!ReplaceTabs)
12149cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek        break;
122735271479ac57c27f744806859efd5b001dea248Chris Lattner      if (EscapeSpaces)
123735271479ac57c27f744806859efd5b001dea248Chris Lattner        RB.ReplaceText(FilePos, 1, "&nbsp;&nbsp;&nbsp;&nbsp;", 6*4);
124735271479ac57c27f744806859efd5b001dea248Chris Lattner      else
125735271479ac57c27f744806859efd5b001dea248Chris Lattner        RB.ReplaceText(FilePos, 1, "    ", 4);
126735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
127735271479ac57c27f744806859efd5b001dea248Chris Lattner
128735271479ac57c27f744806859efd5b001dea248Chris Lattner    case '<':
129735271479ac57c27f744806859efd5b001dea248Chris Lattner      RB.ReplaceText(FilePos, 1, "&lt;", 4);
130735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
131735271479ac57c27f744806859efd5b001dea248Chris Lattner
132735271479ac57c27f744806859efd5b001dea248Chris Lattner    case '>':
133735271479ac57c27f744806859efd5b001dea248Chris Lattner      RB.ReplaceText(FilePos, 1, "&gt;", 4);
134735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
135735271479ac57c27f744806859efd5b001dea248Chris Lattner
136735271479ac57c27f744806859efd5b001dea248Chris Lattner    case '&':
137735271479ac57c27f744806859efd5b001dea248Chris Lattner      RB.ReplaceText(FilePos, 1, "&amp;", 5);
138735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
1396a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek    }
1406a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  }
1416a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek}
1426a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
143fa5be3617294f0e3c341f0ecb6b2076478b1b5acTed Kremenekstd::string html::EscapeText(const std::string& s, bool EscapeSpaces,
144fa5be3617294f0e3c341f0ecb6b2076478b1b5acTed Kremenek                             bool ReplaceTabs) {
145053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek
146053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  unsigned len = s.size();
147053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  std::ostringstream os;
148053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek
149053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  for (unsigned i = 0 ; i < len; ++i) {
150053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek
151053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek    char c = s[i];
152053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek    switch (c) {
1538570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    default:
1548570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      os << c; break;
1558570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner
1568570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    case ' ':
1578570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      if (EscapeSpaces) os << "&nbsp;";
1588570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      else os << ' ';
1598570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      break;
1608570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner
1618570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      case '\t':
1628570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner        if (ReplaceTabs)
1638570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner          for (unsigned i = 0; i < 4; ++i)
1648570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner            os << "&nbsp;";
1658570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner        else
1668570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner          os << c;
1678570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner
168053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek        break;
1698570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner
1708570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      case '<': os << "&lt;"; break;
1718570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      case '>': os << "&gt;"; break;
1728570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      case '&': os << "&amp;"; break;
173053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek    }
174053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  }
175053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek
176053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  return os.str();
177053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek}
178053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek
1798570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattnerstatic void AddLineNumber(RewriteBuffer &RB, unsigned LineNo,
1808570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner                          unsigned B, unsigned E) {
18157df3b950061c73d13d3116f747e79d7955a216aChris Lattner  llvm::SmallString<100> Str;
18257df3b950061c73d13d3116f747e79d7955a216aChris Lattner  Str += "<tr><td class=\"num\" id=\"LN";
18357df3b950061c73d13d3116f747e79d7955a216aChris Lattner  Str.append_uint(LineNo);
18457df3b950061c73d13d3116f747e79d7955a216aChris Lattner  Str += "\">";
18557df3b950061c73d13d3116f747e79d7955a216aChris Lattner  Str.append_uint(LineNo);
18657df3b950061c73d13d3116f747e79d7955a216aChris Lattner  Str += "</td><td class=\"line\">";
18757df3b950061c73d13d3116f747e79d7955a216aChris Lattner
18849cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek  if (B == E) { // Handle empty lines.
18957df3b950061c73d13d3116f747e79d7955a216aChris Lattner    Str += " </td></tr>";
1908570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    RB.InsertTextBefore(B, &Str[0], Str.size());
19157df3b950061c73d13d3116f747e79d7955a216aChris Lattner  } else {
1928570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    RB.InsertTextBefore(B, &Str[0], Str.size());
1938570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    RB.InsertTextBefore(E, "</td></tr>", strlen("</td></tr>"));
19449cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek  }
195b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek}
196b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
197b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenekvoid html::AddLineNumbers(Rewriter& R, unsigned FileID) {
198b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
199b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
200b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  const char* FileBeg = Buf->getBufferStart();
201b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  const char* FileEnd = Buf->getBufferEnd();
202b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  const char* C = FileBeg;
2038570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner  RewriteBuffer &RB = R.getEditBuffer(FileID);
204b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
205b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  assert (C <= FileEnd);
206b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
207b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  unsigned LineNo = 0;
208b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  unsigned FilePos = 0;
209b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
210b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  while (C != FileEnd) {
211b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
212b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    ++LineNo;
213b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    unsigned LineStartPos = FilePos;
214b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    unsigned LineEndPos = FileEnd - FileBeg;
215b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
216b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    assert (FilePos <= LineEndPos);
217b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    assert (C < FileEnd);
218b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
219b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    // Scan until the newline (or end-of-file).
220b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
22149cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek    while (C != FileEnd) {
22249cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek      char c = *C;
22349cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek      ++C;
22449cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek
22549cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek      if (c == '\n') {
22649cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek        LineEndPos = FilePos++;
227b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek        break;
228b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek      }
22949cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek
23049cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek      ++FilePos;
23149cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek    }
232b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
2338570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    AddLineNumber(RB, LineNo, LineStartPos, LineEndPos);
234d6c1360c2bf234c73572a865f119d0518aca8154Ted Kremenek  }
235d6c1360c2bf234c73572a865f119d0518aca8154Ted Kremenek
2368570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner  // Add one big table tag that surrounds all of the code.
2378570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner  RB.InsertTextBefore(0, "<table class=\"code\">\n",
2388570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner                      strlen("<table class=\"code\">\n"));
239d6c1360c2bf234c73572a865f119d0518aca8154Ted Kremenek
2408570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner  RB.InsertTextAfter(FileEnd - FileBeg, "</table>", strlen("</table>"));
241b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek}
242ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
243ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenekvoid html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, unsigned FileID) {
244ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
245ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
246ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  const char* FileStart = Buf->getBufferStart();
247ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  const char* FileEnd = Buf->getBufferEnd();
248ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
249ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  SourceLocation StartLoc = SourceLocation::getFileLoc(FileID, 0);
250ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  SourceLocation EndLoc = SourceLocation::getFileLoc(FileID, FileEnd-FileStart);
251ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
252ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  // Generate header
25370bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek  R.InsertCStrBefore(StartLoc,
25470bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      "<html>\n<head>\n"
25570bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      "<style type=\"text/css\">\n"
25670bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " body { color:#000000; background-color:#ffffff }\n"
25770bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " body { font-family:Helvetica, sans-serif; font-size:10pt }\n"
2584b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      " h1 { font-size:14pt }\n"
25970bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .code { border-spacing:0px; width:100%; }\n"
26070bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .code { font-family: \"Andale Mono\", monospace; font-size:10pt }\n"
26170bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .code { line-height: 1.2em }\n"
2625ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner      " .comment { color: #A0A0A0; font-style: oblique }\n"
263c4586c234edd8df0477a895aebcbc3eb220aed6bChris Lattner      " .keyword { color: #FF00FF }\n"
26474ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner      " .directive { color: #FFFF00 }\n"
265c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner      " .macro { color: #FF0000; background-color:#FFC0C0 }\n"
26670bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .num { width:2.5em; padding-right:2ex; background-color:#eeeeee }\n"
26770bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .num { text-align:right; font-size: smaller }\n"
26870bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .num { color:#444444 }\n"
26970bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .line { padding-left: 1ex; border-left: 3px solid #ccc }\n"
27070bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .line { white-space: pre }\n"
27170bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { background-color:#fff8b4; color:#000000 }\n"
27270bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { -webkit-box-shadow:1px 1px 7px #000 }\n"
27370bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { -webkit-border-radius:5px }\n"
27470bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { font-family:Helvetica, sans-serif; font-size: smaller }\n"
27570bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { font-weight: bold }\n"
27670bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { float:left }\n"
27770bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { padding:0.5em 1ex 0.5em 1ex }\n"
27870bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { margin-top:10px; margin-bottom:10px }\n"
27970bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .mrange { background-color:#dfddf3 }\n"
28070bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .mrange { border-bottom:1px solid #6F9DBE }\n"
28170bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .PathIndex { font-weight: bold }\n"
2824b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      " table.simpletable {\n"
2834b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   padding: 5px;\n"
2844b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   font-size:12pt;\n"
2854b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   margin:20px;\n"
2864b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   border-collapse: collapse; border-spacing: 0px;\n"
2874b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      " }\n"
2884b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      " td.rowname {\n"
2894b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   text-align:right; font-weight:bold; color:#444444;\n"
2904b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   padding-right:2ex; }\n"
29170bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      "</style>\n</head>\n<body>");
29270bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek
293ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  // Generate footer
294ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
29570bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek  R.InsertCStrAfter(EndLoc, "</body></html>\n");
296ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek}
2973245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner
2983245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner/// SyntaxHighlight - Relex the specified FileID and annotate the HTML with
2993245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner/// information about keywords, macro expansions etc.  This uses the macro
3003245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner/// table state from the end of the file, so it won't be perfectly perfect,
3013245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner/// but it will be reasonably close.
3023245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattnervoid html::SyntaxHighlight(Rewriter &R, unsigned FileID, Preprocessor &PP) {
3033245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner  RewriteBuffer &RB = R.getEditBuffer(FileID);
3043245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner
305a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner  const SourceManager &SourceMgr = PP.getSourceManager();
306a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner  std::pair<const char*, const char*> File = SourceMgr.getBufferData(FileID);
307a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner  const char *BufferStart = File.first;
308a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner
309a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner  Lexer L(SourceLocation::getFileLoc(FileID, 0), PP.getLangOptions(),
310a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner          File.first, File.second);
311a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner
3123245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner  // Inform the preprocessor that we want to retain comments as tokens, so we
3133245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner  // can highlight them.
314678c6358c8d4e368c78629099142397c63c1ee35Chris Lattner  L.SetCommentRetentionState(true);
3153245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner
316c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // Lex all the tokens in raw mode, to avoid entering #includes or expanding
317c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // macros.
3183245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner  Token Tok;
319a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner  L.LexRawToken(Tok);
32074ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner
32174ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner  while (Tok.isNot(tok::eof)) {
32274ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner    // Since we are lexing unexpanded tokens, all tokens are from the main
32374ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner    // FileID.
32474ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner    unsigned TokOffs = SourceMgr.getFullFilePos(Tok.getLocation());
3253245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner    unsigned TokLen = Tok.getLength();
3263245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner    switch (Tok.getKind()) {
327a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner    default: break;
328a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner    case tok::identifier: {
329a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner      // Fill in Result.IdentifierInfo, looking up the identifier in the
330a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner      // identifier table.
331a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner      IdentifierInfo *II = PP.LookUpIdentifierInfo(Tok, BufferStart+TokOffs);
332a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner
333a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner      // If this is a pp-identifier, for a keyword, highlight it as such.
3345ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner      if (II->getTokenID() != tok::identifier)
3355ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner        HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
3365ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                       "<span class='keyword'>", "</span>");
337c4586c234edd8df0477a895aebcbc3eb220aed6bChris Lattner      break;
338a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner    }
3393245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner    case tok::comment:
3405ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner      HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
3415ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                     "<span class='comment'>", "</span>");
3423245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner      break;
34374ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner    case tok::hash:
34474ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner      // FIXME: This isn't working because we're not in raw mode in the lexer.
34574ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner      // Just cons up our own lexer here?
34674ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner
34774ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner      // If this is a preprocessor directive, all tokens to end of line are too.
34874ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner      if (Tok.isAtStartOfLine()) {
34974ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner        // Find end of line.  This is a hack.
35074ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner        const char *LineEnd = SourceMgr.getCharacterData(Tok.getLocation());
35174ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner        unsigned TokEnd = TokOffs+strcspn(LineEnd, "\n\r");
3525ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner        HighlightRange(RB, TokOffs, TokEnd, BufferStart,
3535ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                       "<span class='directive'>", "</span>");
35474ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner      }
35574ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner      break;
3563245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner    }
3573245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner
358a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner    L.LexRawToken(Tok);
35974ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner  }
3603245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner}
361c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
362c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner/// HighlightMacros - This uses the macro table state from the end of the
363c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner/// file, to reexpand macros and insert (into the HTML) information about the
364c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner/// macro expansions.  This won't be perfectly perfect, but it will be
365c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner/// reasonably close.
366c54d50a4180520370c12dd7d06d035263d357d56Chris Lattnervoid html::HighlightMacros(Rewriter &R, unsigned FileID, Preprocessor &PP) {
367c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  RewriteBuffer &RB = R.getEditBuffer(FileID);
368c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
369c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // Inform the preprocessor that we don't want comments.
370c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  PP.SetCommentRetentionState(false, false);
371c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
372c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // Start parsing the specified input file.
373c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  PP.EnterMainSourceFile();
374c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
375c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // Lex all the tokens.
376c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  const SourceManager &SourceMgr = PP.getSourceManager();
377c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  Token Tok;
378c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  PP.Lex(Tok);
379c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  while (Tok.isNot(tok::eof)) {
380c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Ignore non-macro tokens.
381c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    if (!Tok.getLocation().isMacroID()) {
382c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner      PP.Lex(Tok);
383c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner      continue;
384c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    }
385c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
386c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Ignore tokens whose logical location was not the main file.
387c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    SourceLocation LLoc = SourceMgr.getLogicalLoc(Tok.getLocation());
388c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    std::pair<unsigned, unsigned> LLocInfo =
389c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner      SourceMgr.getDecomposedFileLoc(LLoc);
390c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
391c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    if (LLocInfo.first != FileID) {
392c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner      PP.Lex(Tok);
393c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner      continue;
394c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    }
395c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
396c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Okay, we have the first token of a macro expansion: highlight the
397c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // instantiation.
398c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
399c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Get the size of current macro call itself.
400c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // FIXME: This should highlight the args of a function-like
401c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // macro, using a heuristic.
402c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    unsigned TokLen = Lexer::MeasureTokenLength(LLoc, SourceMgr);
403c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
404c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    unsigned TokOffs = LLocInfo.second;
405c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    RB.InsertTextAfter(TokOffs, "<span class='macro'>",
406c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner                       strlen("<span class='macro'>"));
407c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    RB.InsertTextBefore(TokOffs+TokLen, "</span>", strlen("</span>"));
408c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
409c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Okay, eat this token, getting the next one.
410c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    PP.Lex(Tok);
411c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
412c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Skip all the rest of the tokens that are part of this macro
413c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // instantiation.  It would be really nice to pop up a window with all the
414c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // spelling of the tokens or something.
415c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    while (!Tok.is(tok::eof) &&
416c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner           SourceMgr.getLogicalLoc(Tok.getLocation()) == LLoc)
417c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner      PP.Lex(Tok);
418c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  }
419c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner}
420c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
421c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
422