blob: 5e4ba971ebefc366f3fd227655759964eaf8e816 [file] [log] [blame]
Ted Kremenek88f5cde2008-03-27 06:17:42 +00001//===--- HTMLDiagnostics.cpp - HTML Diagnostics for Paths ----*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines the HTMLDiagnostics object.
11//
12//===----------------------------------------------------------------------===//
13
14#include "HTMLDiagnostics.h"
15#include "clang/Basic/SourceManager.h"
16#include "clang/AST/ASTContext.h"
17#include "clang/Analysis/PathDiagnostic.h"
18#include "clang/Rewrite/Rewriter.h"
19#include "clang/Rewrite/HTMLRewrite.h"
20#include "clang/Lex/Lexer.h"
21#include "llvm/Support/Compiler.h"
22#include "llvm/Support/MemoryBuffer.h"
23#include "llvm/Support/Streams.h"
24#include "llvm/System/Path.h"
25#include <fstream>
26#include <sstream>
27
28using namespace clang;
29
30//===----------------------------------------------------------------------===//
31// Boilerplate.
32//===----------------------------------------------------------------------===//
33
34namespace {
35
36class VISIBILITY_HIDDEN HTMLDiagnostics : public PathDiagnosticClient {
37 llvm::sys::Path Directory, FilePrefix;
38 bool createdDir, noDir;
39public:
40 HTMLDiagnostics(const std::string& prefix);
41
42 virtual ~HTMLDiagnostics() {}
43
44 virtual void HandlePathDiagnostic(const PathDiagnostic& D);
45
46 void HandlePiece(Rewriter& R, const PathDiagnosticPiece& P, unsigned num);
47 void HighlightRange(Rewriter& R, SourceRange Range, unsigned MainFileID);
48};
49
50} // end anonymous namespace
51
52HTMLDiagnostics::HTMLDiagnostics(const std::string& prefix)
53 : Directory(prefix), FilePrefix(prefix), createdDir(false), noDir(false) {
54
55 // All html files begin with "report"
56 FilePrefix.appendComponent("report");
57}
58
59PathDiagnosticClient*
60clang::CreateHTMLDiagnosticClient(const std::string& prefix) {
61
62 return new HTMLDiagnostics(prefix);
63}
64
65//===----------------------------------------------------------------------===//
66// Report processing.
67//===----------------------------------------------------------------------===//
68
69void HTMLDiagnostics::HandlePathDiagnostic(const PathDiagnostic& D) {
70
71 if (D.empty())
72 return;
73
74 // Create the HTML directory if it is missing.
75
76 if (!createdDir) {
77 createdDir = true;
78 Directory.createDirectoryOnDisk(true, NULL);
79
80 if (!Directory.isDirectory()) {
81 llvm::cerr << "warning: could not create directory '"
82 << FilePrefix.toString() << "'\n";
83
84 noDir = true;
85
86 return;
87 }
88 }
89
90 if (noDir)
91 return;
92
93 // Create a new rewriter to generate HTML.
94 SourceManager& SMgr = D.begin()->getLocation().getManager();
95 Rewriter R(SMgr);
96
97 // Process the path.
98
99 unsigned n = D.size();
100
101 for (PathDiagnostic::const_reverse_iterator I=D.rbegin(), E=D.rend();
102 I!=E; ++I, --n) {
103
104 HandlePiece(R, *I, n);
105 }
106
107 // Add line numbers, header, footer, etc.
108 unsigned FileID = R.getSourceMgr().getMainFileID();
109 html::EscapeText(R, FileID);
110 html::AddLineNumbers(R, FileID);
111
112 // FIXME: Add the number of the file here.
113
114 // Add CSS, header, and footer.
115
116 html::AddHeaderFooterInternalBuiltinCSS(R, FileID);
117
118 // Get the rewrite buffer.
119 const RewriteBuffer *Buf = R.getRewriteBufferFor(FileID);
120
121 if (!Buf) {
122 llvm::cerr << "warning: no diagnostics generated for main file.\n";
123 return;
124 }
125
126 // Create the stream to write out the HTML.
127 std::ofstream os;
128
129 {
130 // Create a path for the target HTML file.
131 llvm::sys::Path F(FilePrefix);
132 F.makeUnique(false, NULL);
133
134 // Rename the file with an HTML extension.
135 llvm::sys::Path H(F);
136 H.appendSuffix("html");
137 F.renamePathOnDisk(H, NULL);
138
139 os.open(H.toString().c_str());
140
141 if (!os) {
142 llvm::cerr << "warning: could not create file '" << F.toString() << "'\n";
143 return;
144 }
145 }
146
147 // Emit the HTML to disk.
148
149 for (RewriteBuffer::iterator I = Buf->begin(), E = Buf->end(); I!=E; ++I)
150 os << *I;
151}
152
153void HTMLDiagnostics::HandlePiece(Rewriter& R,
154 const PathDiagnosticPiece& P,
155 unsigned num) {
156
157 // For now, just draw a box above the line in question, and emit the
158 // warning.
159
160 FullSourceLoc Pos = P.getLocation();
161
162 if (!Pos.isValid())
163 return;
164
165 SourceManager& SM = R.getSourceMgr();
166 FullSourceLoc LPos = Pos.getLogicalLoc();
167 unsigned FileID = LPos.getLocation().getFileID();
168
169 assert (&LPos.getManager() == &SM && "SourceManagers are different!");
170
171 unsigned MainFileID = SM.getMainFileID();
172
173 if (FileID != MainFileID)
174 return;
175
176 // Compute the column number. Rewind from the current position to the start
177 // of the line.
178
179 unsigned ColNo = LPos.getColumnNumber();
180 const char *TokLogicalPtr = LPos.getCharacterData();
181 const char *LineStart = TokLogicalPtr-ColNo;
182
183 // Create the html for the message.
184
185 std::ostringstream os;
186
187 os << "\n<tr><td class=\"num\"></td><td class=\"line\">"
188 << "<div class=\"msg\" style=\"margin-left:"
189 << ColNo << "ex\">";
190
191 os << P.getString() << "</div></td></tr>";
192
193 // Insert the new html.
194
195 const llvm::MemoryBuffer *Buf = SM.getBuffer(FileID);
196 const char* FileStart = Buf->getBufferStart();
197
198 R.InsertStrBefore(SourceLocation::getFileLoc(FileID, LineStart - FileStart),
199 os.str());
200
201 // Now highlight the ranges.
202
203 for (const SourceRange *I = P.ranges_begin(), *E = P.ranges_end();
204 I != E; ++I)
205 HighlightRange(R, *I, MainFileID);
206}
207
208void HTMLDiagnostics::HighlightRange(Rewriter& R, SourceRange Range,
209 unsigned MainFileID) {
210
211 SourceManager& SourceMgr = R.getSourceMgr();
212
213 SourceLocation LogicalStart = SourceMgr.getLogicalLoc(Range.getBegin());
214 unsigned StartLineNo = SourceMgr.getLineNumber(LogicalStart);
215
216 SourceLocation LogicalEnd = SourceMgr.getLogicalLoc(Range.getEnd());
217 unsigned EndLineNo = SourceMgr.getLineNumber(LogicalEnd);
218
219 if (EndLineNo < StartLineNo)
220 return;
221
222 if (LogicalStart.getFileID() != MainFileID ||
223 LogicalEnd.getFileID() != MainFileID)
224 return;
225
226 // Compute the column number of the end.
227 unsigned EndColNo = SourceMgr.getColumnNumber(LogicalEnd);
228 unsigned OldEndColNo = EndColNo;
229
230 if (EndColNo) {
231 // Add in the length of the token, so that we cover multi-char tokens.
232 EndColNo += Lexer::MeasureTokenLength(Range.getEnd(), SourceMgr);
233 }
234
235 // Highlight the range. Make the span tag the outermost tag for the
236 // selected range.
237
238 SourceLocation E =
239 LogicalEnd.getFileLocWithOffset(OldEndColNo > EndColNo
240 ? -(OldEndColNo - EndColNo)
241 : EndColNo - OldEndColNo);
242
243 R.InsertCStrBefore(LogicalStart, "<span class=\"mrange\">");
244 R.InsertCStrAfter(E, "</span>");
245}