Coverage Report

Created: 2021-09-21 08:58

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Rewrite/HTMLRewrite.cpp
Line
Count
Source (jump to first uncovered line)
1
//== HTMLRewrite.cpp - Translate source code into prettified HTML --*- C++ -*-//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
//  This file defines the HTMLRewriter class, which is used to translate the
10
//  text of a source file into prettified HTML.
11
//
12
//===----------------------------------------------------------------------===//
13
14
#include "clang/Rewrite/Core/HTMLRewrite.h"
15
#include "clang/Basic/SourceManager.h"
16
#include "clang/Lex/Preprocessor.h"
17
#include "clang/Lex/TokenConcatenation.h"
18
#include "clang/Rewrite/Core/Rewriter.h"
19
#include "llvm/ADT/SmallString.h"
20
#include "llvm/Support/ErrorHandling.h"
21
#include "llvm/Support/MemoryBuffer.h"
22
#include "llvm/Support/raw_ostream.h"
23
#include <memory>
24
using namespace clang;
25
26
27
/// HighlightRange - Highlight a range in the source code with the specified
28
/// start/end tags.  B/E must be in the same file.  This ensures that
29
/// start/end tags are placed at the start/end of each line if the range is
30
/// multiline.
31
void html::HighlightRange(Rewriter &R, SourceLocation B, SourceLocation E,
32
                          const char *StartTag, const char *EndTag,
33
1.19k
                          bool IsTokenRange) {
34
1.19k
  SourceManager &SM = R.getSourceMgr();
35
1.19k
  B = SM.getExpansionLoc(B);
36
1.19k
  E = SM.getExpansionLoc(E);
37
1.19k
  FileID FID = SM.getFileID(B);
38
1.19k
  assert(SM.getFileID(E) == FID && "B/E not in the same file!");
39
40
0
  unsigned BOffset = SM.getFileOffset(B);
41
1.19k
  unsigned EOffset = SM.getFileOffset(E);
42
43
  // Include the whole end token in the range.
44
1.19k
  if (IsTokenRange)
45
1.19k
    EOffset += Lexer::MeasureTokenLength(E, R.getSourceMgr(), R.getLangOpts());
46
47
1.19k
  bool Invalid = false;
48
1.19k
  const char *BufferStart = SM.getBufferData(FID, &Invalid).data();
49
1.19k
  if (Invalid)
50
0
    return;
51
52
1.19k
  HighlightRange(R.getEditBuffer(FID), BOffset, EOffset,
53
1.19k
                 BufferStart, StartTag, EndTag);
54
1.19k
}
55
56
/// HighlightRange - This is the same as the above method, but takes
57
/// decomposed file locations.
58
void html::HighlightRange(RewriteBuffer &RB, unsigned B, unsigned E,
59
                          const char *BufferStart,
60
16.8k
                          const char *StartTag, const char *EndTag) {
61
  // Insert the tag at the absolute start/end of the range.
62
16.8k
  RB.InsertTextAfter(B, StartTag);
63
16.8k
  RB.InsertTextBefore(E, EndTag);
64
65
  // Scan the range to see if there is a \r or \n.  If so, and if the line is
66
  // not blank, insert tags on that line as well.
67
16.8k
  bool HadOpenTag = true;
68
69
16.8k
  unsigned LastNonWhiteSpace = B;
70
252k
  for (unsigned i = B; i != E; 
++i235k
) {
71
235k
    switch (BufferStart[i]) {
72
0
    case '\r':
73
50
    case '\n':
74
      // Okay, we found a newline in the range.  If we have an open tag, we need
75
      // to insert a close tag at the first non-whitespace before the newline.
76
50
      if (HadOpenTag)
77
50
        RB.InsertTextBefore(LastNonWhiteSpace+1, EndTag);
78
79
      // Instead of inserting an open tag immediately after the newline, we
80
      // wait until we see a non-whitespace character.  This prevents us from
81
      // inserting tags around blank lines, and also allows the open tag to
82
      // be put *after* whitespace on a non-blank line.
83
50
      HadOpenTag = false;
84
50
      break;
85
0
    case '\0':
86
16.8k
    case ' ':
87
16.8k
    case '\t':
88
16.8k
    case '\f':
89
16.8k
    case '\v':
90
      // Ignore whitespace.
91
16.8k
      break;
92
93
218k
    default:
94
      // If there is no tag open, do it now.
95
218k
      if (!HadOpenTag) {
96
50
        RB.InsertTextAfter(i, StartTag);
97
50
        HadOpenTag = true;
98
50
      }
99
100
      // Remember this character.
101
218k
      LastNonWhiteSpace = i;
102
218k
      break;
103
235k
    }
104
235k
  }
105
16.8k
}
106
107
void html::EscapeText(Rewriter &R, FileID FID,
108
117
                      bool EscapeSpaces, bool ReplaceTabs) {
109
110
117
  llvm::MemoryBufferRef Buf = R.getSourceMgr().getBufferOrFake(FID);
111
117
  const char* C = Buf.getBufferStart();
112
117
  const char* FileEnd = Buf.getBufferEnd();
113
114
117
  assert (C <= FileEnd);
115
116
0
  RewriteBuffer &RB = R.getEditBuffer(FID);
117
118
117
  unsigned ColNo = 0;
119
527k
  for (unsigned FilePos = 0; C != FileEnd ; 
++C, ++FilePos527k
) {
120
527k
    switch (*C) {
121
430k
    default: ++ColNo; break;
122
22.3k
    case '\n':
123
22.3k
    case '\r':
124
22.3k
      ColNo = 0;
125
22.3k
      break;
126
127
71.7k
    case ' ':
128
71.7k
      if (EscapeSpaces)
129
0
        RB.ReplaceText(FilePos, 1, "&nbsp;");
130
71.7k
      ++ColNo;
131
71.7k
      break;
132
0
    case '\f':
133
0
      RB.ReplaceText(FilePos, 1, "<hr>");
134
0
      ColNo = 0;
135
0
      break;
136
137
80
    case '\t': {
138
80
      if (!ReplaceTabs)
139
80
        break;
140
0
      unsigned NumSpaces = 8-(ColNo&7);
141
0
      if (EscapeSpaces)
142
0
        RB.ReplaceText(FilePos, 1,
143
0
                       StringRef("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
144
0
                                       "&nbsp;&nbsp;&nbsp;", 6*NumSpaces));
145
0
      else
146
0
        RB.ReplaceText(FilePos, 1, StringRef("        ", NumSpaces));
147
0
      ColNo += NumSpaces;
148
0
      break;
149
80
    }
150
896
    case '<':
151
896
      RB.ReplaceText(FilePos, 1, "&lt;");
152
896
      ++ColNo;
153
896
      break;
154
155
638
    case '>':
156
638
      RB.ReplaceText(FilePos, 1, "&gt;");
157
638
      ++ColNo;
158
638
      break;
159
160
1.89k
    case '&':
161
1.89k
      RB.ReplaceText(FilePos, 1, "&amp;");
162
1.89k
      ++ColNo;
163
1.89k
      break;
164
527k
    }
165
527k
  }
166
117
}
167
168
1.28k
std::string html::EscapeText(StringRef s, bool EscapeSpaces, bool ReplaceTabs) {
169
170
1.28k
  unsigned len = s.size();
171
1.28k
  std::string Str;
172
1.28k
  llvm::raw_string_ostream os(Str);
173
174
134k
  for (unsigned i = 0 ; i < len; 
++i133k
) {
175
176
133k
    char c = s[i];
177
133k
    switch (c) {
178
128k
    default:
179
128k
      os << c; break;
180
181
4.88k
    case ' ':
182
4.88k
      if (EscapeSpaces) 
os << "&nbsp;"0
;
183
4.88k
      else os << ' ';
184
4.88k
      break;
185
186
20
    case '\t':
187
20
      if (ReplaceTabs) {
188
0
        if (EscapeSpaces)
189
0
          for (unsigned i = 0; i < 4; ++i)
190
0
            os << "&nbsp;";
191
0
        else
192
0
          for (unsigned i = 0; i < 4; ++i)
193
0
            os << " ";
194
0
      }
195
20
      else
196
20
        os << c;
197
198
20
      break;
199
200
13
    case '<': os << "&lt;"; break;
201
15
    case '>': os << "&gt;"; break;
202
1
    case '&': os << "&amp;"; break;
203
133k
    }
204
133k
  }
205
206
1.28k
  return os.str();
207
1.28k
}
208
209
static void AddLineNumber(RewriteBuffer &RB, unsigned LineNo,
210
22.3k
                          unsigned B, unsigned E) {
211
22.3k
  SmallString<256> Str;
212
22.3k
  llvm::raw_svector_ostream OS(Str);
213
214
22.3k
  OS << "<tr class=\"codeline\" data-linenumber=\"" << LineNo << "\">"
215
22.3k
     << "<td class=\"num\" id=\"LN" << LineNo << "\">" << LineNo
216
22.3k
     << "</td><td class=\"line\">";
217
218
22.3k
  if (B == E) { // Handle empty lines.
219
3.63k
    OS << " </td></tr>";
220
3.63k
    RB.InsertTextBefore(B, OS.str());
221
18.7k
  } else {
222
18.7k
    RB.InsertTextBefore(B, OS.str());
223
18.7k
    RB.InsertTextBefore(E, "</td></tr>");
224
18.7k
  }
225
22.3k
}
226
227
117
void html::AddLineNumbers(Rewriter& R, FileID FID) {
228
229
117
  llvm::MemoryBufferRef Buf = R.getSourceMgr().getBufferOrFake(FID);
230
117
  const char* FileBeg = Buf.getBufferStart();
231
117
  const char* FileEnd = Buf.getBufferEnd();
232
117
  const char* C = FileBeg;
233
117
  RewriteBuffer &RB = R.getEditBuffer(FID);
234
235
117
  assert (C <= FileEnd);
236
237
0
  unsigned LineNo = 0;
238
117
  unsigned FilePos = 0;
239
240
22.4k
  while (C != FileEnd) {
241
242
22.3k
    ++LineNo;
243
22.3k
    unsigned LineStartPos = FilePos;
244
22.3k
    unsigned LineEndPos = FileEnd - FileBeg;
245
246
22.3k
    assert (FilePos <= LineEndPos);
247
0
    assert (C < FileEnd);
248
249
    // Scan until the newline (or end-of-file).
250
251
527k
    while (C != FileEnd) {
252
527k
      char c = *C;
253
527k
      ++C;
254
255
527k
      if (c == '\n') {
256
22.3k
        LineEndPos = FilePos++;
257
22.3k
        break;
258
22.3k
      }
259
260
505k
      ++FilePos;
261
505k
    }
262
263
22.3k
    AddLineNumber(RB, LineNo, LineStartPos, LineEndPos);
264
22.3k
  }
265
266
  // Add one big table tag that surrounds all of the code.
267
117
  std::string s;
268
117
  llvm::raw_string_ostream os(s);
269
117
  os << "<table class=\"code\" data-fileid=\"" << FID.getHashValue() << "\">\n";
270
117
  RB.InsertTextBefore(0, os.str());
271
117
  RB.InsertTextAfter(FileEnd - FileBeg, "</table>");
272
117
}
273
274
void html::AddHeaderFooterInternalBuiltinCSS(Rewriter &R, FileID FID,
275
107
                                             StringRef title) {
276
277
107
  llvm::MemoryBufferRef Buf = R.getSourceMgr().getBufferOrFake(FID);
278
107
  const char* FileStart = Buf.getBufferStart();
279
107
  const char* FileEnd = Buf.getBufferEnd();
280
281
107
  SourceLocation StartLoc = R.getSourceMgr().getLocForStartOfFile(FID);
282
107
  SourceLocation EndLoc = StartLoc.getLocWithOffset(FileEnd-FileStart);
283
284
107
  std::string s;
285
107
  llvm::raw_string_ostream os(s);
286
107
  os << "<!doctype html>\n" // Use HTML 5 doctype
287
107
        "<html>\n<head>\n";
288
289
107
  if (!title.empty())
290
107
    os << "<title>" << html::EscapeText(title) << "</title>\n";
291
292
107
  os << R"<<<(
293
107
<style type="text/css">
294
107
body { color:#000000; background-color:#ffffff }
295
107
body { font-family:Helvetica, sans-serif; font-size:10pt }
296
107
h1 { font-size:14pt }
297
107
.FileName { margin-top: 5px; margin-bottom: 5px; display: inline; }
298
107
.FileNav { margin-left: 5px; margin-right: 5px; display: inline; }
299
107
.FileNav a { text-decoration:none; font-size: larger; }
300
107
.divider { margin-top: 30px; margin-bottom: 30px; height: 15px; }
301
107
.divider { background-color: gray; }
302
107
.code { border-collapse:collapse; width:100%; }
303
107
.code { font-family: "Monospace", monospace; font-size:10pt }
304
107
.code { line-height: 1.2em }
305
107
.comment { color: green; font-style: oblique }
306
107
.keyword { color: blue }
307
107
.string_literal { color: red }
308
107
.directive { color: darkmagenta }
309
107
310
107
/* Macros and variables could have pop-up notes hidden by default.
311
107
  - Macro pop-up:    expansion of the macro
312
107
  - Variable pop-up: value (table) of the variable */
313
107
.macro_popup, .variable_popup { display: none; }
314
107
315
107
/* Pop-up appears on mouse-hover event. */
316
107
.macro:hover .macro_popup, .variable:hover .variable_popup {
317
107
  display: block;
318
107
  padding: 2px;
319
107
  -webkit-border-radius:5px;
320
107
  -webkit-box-shadow:1px 1px 7px #000;
321
107
  border-radius:5px;
322
107
  box-shadow:1px 1px 7px #000;
323
107
  position: absolute;
324
107
  top: -1em;
325
107
  left:10em;
326
107
  z-index: 1
327
107
}
328
107
329
107
.macro_popup {
330
107
  border: 2px solid red;
331
107
  background-color:#FFF0F0;
332
107
  font-weight: normal;
333
107
}
334
107
335
107
.variable_popup {
336
107
  border: 2px solid blue;
337
107
  background-color:#F0F0FF;
338
107
  font-weight: bold;
339
107
  font-family: Helvetica, sans-serif;
340
107
  font-size: 9pt;
341
107
}
342
107
343
107
/* Pop-up notes needs a relative position as a base where they pops up. */
344
107
.macro, .variable {
345
107
  background-color: PaleGoldenRod;
346
107
  position: relative;
347
107
}
348
107
.macro { color: DarkMagenta; }
349
107
350
107
#tooltiphint {
351
107
  position: fixed;
352
107
  width: 50em;
353
107
  margin-left: -25em;
354
107
  left: 50%;
355
107
  padding: 10px;
356
107
  border: 1px solid #b0b0b0;
357
107
  border-radius: 2px;
358
107
  box-shadow: 1px 1px 7px black;
359
107
  background-color: #c0c0c0;
360
107
  z-index: 2;
361
107
}
362
107
363
107
.num { width:2.5em; padding-right:2ex; background-color:#eeeeee }
364
107
.num { text-align:right; font-size:8pt }
365
107
.num { color:#444444 }
366
107
.line { padding-left: 1ex; border-left: 3px solid #ccc }
367
107
.line { white-space: pre }
368
107
.msg { -webkit-box-shadow:1px 1px 7px #000 }
369
107
.msg { box-shadow:1px 1px 7px #000 }
370
107
.msg { -webkit-border-radius:5px }
371
107
.msg { border-radius:5px }
372
107
.msg { font-family:Helvetica, sans-serif; font-size:8pt }
373
107
.msg { float:left }
374
107
.msg { position:relative }
375
107
.msg { padding:0.25em 1ex 0.25em 1ex }
376
107
.msg { margin-top:10px; margin-bottom:10px }
377
107
.msg { font-weight:bold }
378
107
.msg { max-width:60em; word-wrap: break-word; white-space: pre-wrap }
379
107
.msgT { padding:0x; spacing:0x }
380
107
.msgEvent { background-color:#fff8b4; color:#000000 }
381
107
.msgControl { background-color:#bbbbbb; color:#000000 }
382
107
.msgNote { background-color:#ddeeff; color:#000000 }
383
107
.mrange { background-color:#dfddf3 }
384
107
.mrange { border-bottom:1px solid #6F9DBE }
385
107
.PathIndex { font-weight: bold; padding:0px 5px; margin-right:5px; }
386
107
.PathIndex { -webkit-border-radius:8px }
387
107
.PathIndex { border-radius:8px }
388
107
.PathIndexEvent { background-color:#bfba87 }
389
107
.PathIndexControl { background-color:#8c8c8c }
390
107
.PathIndexPopUp { background-color: #879abc; }
391
107
.PathNav a { text-decoration:none; font-size: larger }
392
107
.CodeInsertionHint { font-weight: bold; background-color: #10dd10 }
393
107
.CodeRemovalHint { background-color:#de1010 }
394
107
.CodeRemovalHint { border-bottom:1px solid #6F9DBE }
395
107
.msg.selected{ background-color:orange !important; }
396
107
397
107
table.simpletable {
398
107
  padding: 5px;
399
107
  font-size:12pt;
400
107
  margin:20px;
401
107
  border-collapse: collapse; border-spacing: 0px;
402
107
}
403
107
td.rowname {
404
107
  text-align: right;
405
107
  vertical-align: top;
406
107
  font-weight: bold;
407
107
  color:#444444;
408
107
  padding-right:2ex;
409
107
}
410
107
411
107
/* Hidden text. */
412
107
input.spoilerhider + label {
413
107
  cursor: pointer;
414
107
  text-decoration: underline;
415
107
  display: block;
416
107
}
417
107
input.spoilerhider {
418
107
 display: none;
419
107
}
420
107
input.spoilerhider ~ .spoiler {
421
107
  overflow: hidden;
422
107
  margin: 10px auto 0;
423
107
  height: 0;
424
107
  opacity: 0;
425
107
}
426
107
input.spoilerhider:checked + label + .spoiler{
427
107
  height: auto;
428
107
  opacity: 1;
429
107
}
430
107
</style>
431
107
</head>
432
107
<body>)<<<";
433
434
  // Generate header
435
107
  R.InsertTextBefore(StartLoc, os.str());
436
  // Generate footer
437
438
107
  R.InsertTextAfter(EndLoc, "</body></html>\n");
439
107
}
440
441
/// SyntaxHighlight - Relex the specified FileID and annotate the HTML with
442
/// information about keywords, macro expansions etc.  This uses the macro
443
/// table state from the end of the file, so it won't be perfectly perfect,
444
/// but it will be reasonably close.
445
117
void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) {
446
117
  RewriteBuffer &RB = R.getEditBuffer(FID);
447
448
117
  const SourceManager &SM = PP.getSourceManager();
449
117
  llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
450
117
  Lexer L(FID, FromFile, SM, PP.getLangOpts());
451
117
  const char *BufferStart = L.getBuffer().data();
452
453
  // Inform the preprocessor that we want to retain comments as tokens, so we
454
  // can highlight them.
455
117
  L.SetCommentRetentionState(true);
456
457
  // Lex all the tokens in raw mode, to avoid entering #includes or expanding
458
  // macros.
459
117
  Token Tok;
460
117
  L.LexFromRawLexer(Tok);
461
462
93.2k
  while (Tok.isNot(tok::eof)) {
463
    // Since we are lexing unexpanded tokens, all tokens are from the main
464
    // FileID.
465
93.1k
    unsigned TokOffs = SM.getFileOffset(Tok.getLocation());
466
93.1k
    unsigned TokLen = Tok.getLength();
467
93.1k
    switch (Tok.getKind()) {
468
53.9k
    default: break;
469
53.9k
    case tok::identifier:
470
0
      llvm_unreachable("tok::identifier in raw lexing mode!");
471
33.9k
    case tok::raw_identifier: {
472
      // Fill in Result.IdentifierInfo and update the token kind,
473
      // looking up the identifier in the identifier table.
474
33.9k
      PP.LookUpIdentifierInfo(Tok);
475
476
      // If this is a pp-identifier, for a keyword, highlight it as such.
477
33.9k
      if (Tok.isNot(tok::identifier))
478
10.3k
        HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
479
10.3k
                       "<span class='keyword'>", "</span>");
480
33.9k
      break;
481
0
    }
482
2.97k
    case tok::comment:
483
2.97k
      HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
484
2.97k
                     "<span class='comment'>", "</span>");
485
2.97k
      break;
486
0
    case tok::utf8_string_literal:
487
      // Chop off the u part of u8 prefix
488
0
      ++TokOffs;
489
0
      --TokLen;
490
      // FALL THROUGH to chop the 8
491
0
      LLVM_FALLTHROUGH;
492
0
    case tok::wide_string_literal:
493
0
    case tok::utf16_string_literal:
494
0
    case tok::utf32_string_literal:
495
      // Chop off the L, u, U or 8 prefix
496
0
      ++TokOffs;
497
0
      --TokLen;
498
0
      LLVM_FALLTHROUGH;
499
38
    case tok::string_literal:
500
      // FIXME: Exclude the optional ud-suffix from the highlighted range.
501
38
      HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
502
38
                     "<span class='string_literal'>", "</span>");
503
38
      break;
504
2.26k
    case tok::hash: {
505
      // If this is a preprocessor directive, all tokens to end of line are too.
506
2.26k
      if (!Tok.isAtStartOfLine())
507
0
        break;
508
509
      // Eat all of the tokens until we get to the next one at the start of
510
      // line.
511
2.26k
      unsigned TokEnd = TokOffs+TokLen;
512
2.26k
      L.LexFromRawLexer(Tok);
513
5.80k
      while (!Tok.isAtStartOfLine() && 
Tok.isNot(tok::eof)3.53k
) {
514
3.53k
        TokEnd = SM.getFileOffset(Tok.getLocation())+Tok.getLength();
515
3.53k
        L.LexFromRawLexer(Tok);
516
3.53k
      }
517
518
      // Find end of line.  This is a hack.
519
2.26k
      HighlightRange(RB, TokOffs, TokEnd, BufferStart,
520
2.26k
                     "<span class='directive'>", "</span>");
521
522
      // Don't skip the next token.
523
2.26k
      continue;
524
2.26k
    }
525
93.1k
    }
526
527
90.8k
    L.LexFromRawLexer(Tok);
528
90.8k
  }
529
117
}
530
531
/// HighlightMacros - This uses the macro table state from the end of the
532
/// file, to re-expand macros and insert (into the HTML) information about the
533
/// macro expansions.  This won't be perfectly perfect, but it will be
534
/// reasonably close.
535
117
void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) {
536
  // Re-lex the raw token stream into a token buffer.
537
117
  const SourceManager &SM = PP.getSourceManager();
538
117
  std::vector<Token> TokenStream;
539
540
117
  llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
541
117
  Lexer L(FID, FromFile, SM, PP.getLangOpts());
542
543
  // Lex all the tokens in raw mode, to avoid entering #includes or expanding
544
  // macros.
545
93.8k
  while (1) {
546
93.8k
    Token Tok;
547
93.8k
    L.LexFromRawLexer(Tok);
548
549
    // If this is a # at the start of a line, discard it from the token stream.
550
    // We don't want the re-preprocess step to see #defines, #includes or other
551
    // preprocessor directives.
552
93.8k
    if (Tok.is(tok::hash) && 
Tok.isAtStartOfLine()2.26k
)
553
2.26k
      continue;
554
555
    // If this is a ## token, change its kind to unknown so that repreprocessing
556
    // it will not produce an error.
557
91.5k
    if (Tok.is(tok::hashhash))
558
1
      Tok.setKind(tok::unknown);
559
560
    // If this raw token is an identifier, the raw lexer won't have looked up
561
    // the corresponding identifier info for it.  Do this now so that it will be
562
    // macro expanded when we re-preprocess it.
563
91.5k
    if (Tok.is(tok::raw_identifier))
564
37.3k
      PP.LookUpIdentifierInfo(Tok);
565
566
91.5k
    TokenStream.push_back(Tok);
567
568
91.5k
    if (Tok.is(tok::eof)) 
break117
;
569
91.5k
  }
570
571
  // Temporarily change the diagnostics object so that we ignore any generated
572
  // diagnostics from this pass.
573
117
  DiagnosticsEngine TmpDiags(PP.getDiagnostics().getDiagnosticIDs(),
574
117
                             &PP.getDiagnostics().getDiagnosticOptions(),
575
117
                      new IgnoringDiagConsumer);
576
577
  // FIXME: This is a huge hack; we reuse the input preprocessor because we want
578
  // its state, but we aren't actually changing it (we hope). This should really
579
  // construct a copy of the preprocessor.
580
117
  Preprocessor &TmpPP = const_cast<Preprocessor&>(PP);
581
117
  DiagnosticsEngine *OldDiags = &TmpPP.getDiagnostics();
582
117
  TmpPP.setDiagnostics(TmpDiags);
583
584
  // Inform the preprocessor that we don't want comments.
585
117
  TmpPP.SetCommentRetentionState(false, false);
586
587
  // We don't want pragmas either. Although we filtered out #pragma, removing
588
  // _Pragma and __pragma is much harder.
589
117
  bool PragmasPreviouslyEnabled = TmpPP.getPragmasEnabled();
590
117
  TmpPP.setPragmasEnabled(false);
591
592
  // Enter the tokens we just lexed.  This will cause them to be macro expanded
593
  // but won't enter sub-files (because we removed #'s).
594
117
  TmpPP.EnterTokenStream(TokenStream, false, /*IsReinject=*/false);
595
596
117
  TokenConcatenation ConcatInfo(TmpPP);
597
598
  // Lex all the tokens.
599
117
  Token Tok;
600
117
  TmpPP.Lex(Tok);
601
91.4k
  while (Tok.isNot(tok::eof)) {
602
    // Ignore non-macro tokens.
603
91.3k
    if (!Tok.getLocation().isMacroID()) {
604
90.9k
      TmpPP.Lex(Tok);
605
90.9k
      continue;
606
90.9k
    }
607
608
    // Okay, we have the first token of a macro expansion: highlight the
609
    // expansion by inserting a start tag before the macro expansion and
610
    // end tag after it.
611
372
    CharSourceRange LLoc = SM.getExpansionRange(Tok.getLocation());
612
613
    // Ignore tokens whose instantiation location was not the main file.
614
372
    if (SM.getFileID(LLoc.getBegin()) != FID) {
615
0
      TmpPP.Lex(Tok);
616
0
      continue;
617
0
    }
618
619
372
    assert(SM.getFileID(LLoc.getEnd()) == FID &&
620
372
           "Start and end of expansion must be in the same ultimate file!");
621
622
0
    std::string Expansion = EscapeText(TmpPP.getSpelling(Tok));
623
372
    unsigned LineLen = Expansion.size();
624
625
372
    Token PrevPrevTok;
626
372
    Token PrevTok = Tok;
627
    // Okay, eat this token, getting the next one.
628
372
    TmpPP.Lex(Tok);
629
630
    // Skip all the rest of the tokens that are part of this macro
631
    // instantiation.  It would be really nice to pop up a window with all the
632
    // spelling of the tokens or something.
633
595
    while (!Tok.is(tok::eof) &&
634
595
           
SM.getExpansionLoc(Tok.getLocation()) == LLoc.getBegin()594
) {
635
      // Insert a newline if the macro expansion is getting large.
636
223
      if (LineLen > 60) {
637
0
        Expansion += "<br>";
638
0
        LineLen = 0;
639
0
      }
640
641
223
      LineLen -= Expansion.size();
642
643
      // If the tokens were already space separated, or if they must be to avoid
644
      // them being implicitly pasted, add a space between them.
645
223
      if (Tok.hasLeadingSpace() ||
646
223
          
ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, Tok)192
)
647
31
        Expansion += ' ';
648
649
      // Escape any special characters in the token text.
650
223
      Expansion += EscapeText(TmpPP.getSpelling(Tok));
651
223
      LineLen += Expansion.size();
652
653
223
      PrevPrevTok = PrevTok;
654
223
      PrevTok = Tok;
655
223
      TmpPP.Lex(Tok);
656
223
    }
657
658
    // Insert the 'macro_popup' as the end tag, so that multi-line macros all
659
    // get highlighted.
660
372
    Expansion = "<span class='macro_popup'>" + Expansion + "</span></span>";
661
662
372
    HighlightRange(R, LLoc.getBegin(), LLoc.getEnd(), "<span class='macro'>",
663
372
                   Expansion.c_str(), LLoc.isTokenRange());
664
372
  }
665
666
  // Restore the preprocessor's old state.
667
117
  TmpPP.setDiagnostics(*OldDiags);
668
117
  TmpPP.setPragmasEnabled(PragmasPreviouslyEnabled);
669
117
}