1 /*===-- CIndexDiagnostics.cpp - Diagnostics C Interface ---------*- 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 |* Implements the diagnostic functions of the Clang C interface. *|
11 |* *|
12 \*===----------------------------------------------------------------------===*/
13 #include "CIndexDiagnostic.h"
14 #include "CIndexer.h"
15 #include "CXTranslationUnit.h"
16 #include "CXSourceLocation.h"
17 #include "CXString.h"
18
19 #include "clang/Frontend/ASTUnit.h"
20 #include "clang/Frontend/FrontendDiagnostic.h"
21 #include "clang/Frontend/DiagnosticRenderer.h"
22 #include "clang/Frontend/DiagnosticOptions.h"
23 #include "llvm/ADT/SmallString.h"
24 #include "llvm/ADT/Twine.h"
25 #include "llvm/Support/MemoryBuffer.h"
26 #include "llvm/Support/raw_ostream.h"
27
28 using namespace clang;
29 using namespace clang::cxloc;
30 using namespace clang::cxstring;
31 using namespace clang::cxdiag;
32 using namespace llvm;
33
34
~CXDiagnosticSetImpl()35 CXDiagnosticSetImpl::~CXDiagnosticSetImpl() {
36 for (std::vector<CXDiagnosticImpl *>::iterator it = Diagnostics.begin(),
37 et = Diagnostics.end();
38 it != et; ++it) {
39 delete *it;
40 }
41 }
42
~CXDiagnosticImpl()43 CXDiagnosticImpl::~CXDiagnosticImpl() {}
44
45 namespace {
46 class CXDiagnosticCustomNoteImpl : public CXDiagnosticImpl {
47 std::string Message;
48 CXSourceLocation Loc;
49 public:
CXDiagnosticCustomNoteImpl(StringRef Msg,CXSourceLocation L)50 CXDiagnosticCustomNoteImpl(StringRef Msg, CXSourceLocation L)
51 : CXDiagnosticImpl(CustomNoteDiagnosticKind),
52 Message(Msg), Loc(L) {}
53
~CXDiagnosticCustomNoteImpl()54 virtual ~CXDiagnosticCustomNoteImpl() {}
55
getSeverity() const56 CXDiagnosticSeverity getSeverity() const {
57 return CXDiagnostic_Note;
58 }
59
getLocation() const60 CXSourceLocation getLocation() const {
61 return Loc;
62 }
63
getSpelling() const64 CXString getSpelling() const {
65 return createCXString(StringRef(Message), false);
66 }
67
getDiagnosticOption(CXString * Disable) const68 CXString getDiagnosticOption(CXString *Disable) const {
69 if (Disable)
70 *Disable = createCXString("", false);
71 return createCXString("", false);
72 }
73
getCategory() const74 unsigned getCategory() const { return 0; }
getCategoryText() const75 CXString getCategoryText() const { return createCXString(""); }
76
getNumRanges() const77 unsigned getNumRanges() const { return 0; }
getRange(unsigned Range) const78 CXSourceRange getRange(unsigned Range) const { return clang_getNullRange(); }
getNumFixIts() const79 unsigned getNumFixIts() const { return 0; }
getFixIt(unsigned FixIt,CXSourceRange * ReplacementRange) const80 CXString getFixIt(unsigned FixIt, CXSourceRange *ReplacementRange) const {
81 if (ReplacementRange)
82 *ReplacementRange = clang_getNullRange();
83 return createCXString("", false);
84 }
85 };
86
87 class CXDiagnosticRenderer : public DiagnosticNoteRenderer {
88 public:
CXDiagnosticRenderer(const SourceManager & SM,const LangOptions & LangOpts,const DiagnosticOptions & DiagOpts,CXDiagnosticSetImpl * mainSet)89 CXDiagnosticRenderer(const SourceManager &SM,
90 const LangOptions &LangOpts,
91 const DiagnosticOptions &DiagOpts,
92 CXDiagnosticSetImpl *mainSet)
93 : DiagnosticNoteRenderer(SM, LangOpts, DiagOpts),
94 CurrentSet(mainSet), MainSet(mainSet) {}
95
~CXDiagnosticRenderer()96 virtual ~CXDiagnosticRenderer() {}
97
beginDiagnostic(DiagOrStoredDiag D,DiagnosticsEngine::Level Level)98 virtual void beginDiagnostic(DiagOrStoredDiag D,
99 DiagnosticsEngine::Level Level) {
100
101 const StoredDiagnostic *SD = D.dyn_cast<const StoredDiagnostic*>();
102 if (!SD)
103 return;
104
105 if (Level != DiagnosticsEngine::Note)
106 CurrentSet = MainSet;
107
108 CXStoredDiagnostic *CD = new CXStoredDiagnostic(*SD, LangOpts);
109 CurrentSet->appendDiagnostic(CD);
110
111 if (Level != DiagnosticsEngine::Note)
112 CurrentSet = &CD->getChildDiagnostics();
113 }
114
emitDiagnosticMessage(SourceLocation Loc,PresumedLoc PLoc,DiagnosticsEngine::Level Level,StringRef Message,ArrayRef<CharSourceRange> Ranges,DiagOrStoredDiag D)115 virtual void emitDiagnosticMessage(SourceLocation Loc, PresumedLoc PLoc,
116 DiagnosticsEngine::Level Level,
117 StringRef Message,
118 ArrayRef<CharSourceRange> Ranges,
119 DiagOrStoredDiag D) {
120 if (!D.isNull())
121 return;
122
123 CXSourceLocation L = translateSourceLocation(SM, LangOpts, Loc);
124 CXDiagnosticImpl *CD = new CXDiagnosticCustomNoteImpl(Message, L);
125 CurrentSet->appendDiagnostic(CD);
126 }
127
emitDiagnosticLoc(SourceLocation Loc,PresumedLoc PLoc,DiagnosticsEngine::Level Level,ArrayRef<CharSourceRange> Ranges)128 virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
129 DiagnosticsEngine::Level Level,
130 ArrayRef<CharSourceRange> Ranges) {}
131
emitCodeContext(SourceLocation Loc,DiagnosticsEngine::Level Level,SmallVectorImpl<CharSourceRange> & Ranges,ArrayRef<FixItHint> Hints)132 virtual void emitCodeContext(SourceLocation Loc,
133 DiagnosticsEngine::Level Level,
134 SmallVectorImpl<CharSourceRange>& Ranges,
135 ArrayRef<FixItHint> Hints) {}
136
emitNote(SourceLocation Loc,StringRef Message)137 virtual void emitNote(SourceLocation Loc, StringRef Message) {
138 CXSourceLocation L = translateSourceLocation(SM, LangOpts, Loc);
139 CurrentSet->appendDiagnostic(new CXDiagnosticCustomNoteImpl(Message,
140 L));
141 }
142
143 CXDiagnosticSetImpl *CurrentSet;
144 CXDiagnosticSetImpl *MainSet;
145 };
146 }
147
lazyCreateDiags(CXTranslationUnit TU,bool checkIfChanged)148 CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU,
149 bool checkIfChanged) {
150 ASTUnit *AU = static_cast<ASTUnit *>(TU->TUData);
151
152 if (TU->Diagnostics && checkIfChanged) {
153 // In normal use, ASTUnit's diagnostics should not change unless we reparse.
154 // Currently they can only change by using the internal testing flag
155 // '-error-on-deserialized-decl' which will error during deserialization of
156 // a declaration. What will happen is:
157 //
158 // -c-index-test gets a CXTranslationUnit
159 // -checks the diagnostics, the diagnostics set is lazily created,
160 // no errors are reported
161 // -later does an operation, like annotation of tokens, that triggers
162 // -error-on-deserialized-decl, that will emit a diagnostic error,
163 // that ASTUnit will catch and add to its stored diagnostics vector.
164 // -c-index-test wants to check whether an error occurred after performing
165 // the operation but can only query the lazily created set.
166 //
167 // We check here if a new diagnostic was appended since the last time the
168 // diagnostic set was created, in which case we reset it.
169
170 CXDiagnosticSetImpl *
171 Set = static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
172 if (AU->stored_diag_size() != Set->getNumDiagnostics()) {
173 // Diagnostics in the ASTUnit were updated, reset the associated
174 // diagnostics.
175 delete Set;
176 TU->Diagnostics = 0;
177 }
178 }
179
180 if (!TU->Diagnostics) {
181 CXDiagnosticSetImpl *Set = new CXDiagnosticSetImpl();
182 TU->Diagnostics = Set;
183 DiagnosticOptions DOpts;
184 CXDiagnosticRenderer Renderer(AU->getSourceManager(),
185 AU->getASTContext().getLangOpts(),
186 DOpts, Set);
187
188 for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(),
189 ei = AU->stored_diag_end(); it != ei; ++it) {
190 Renderer.emitStoredDiagnostic(*it);
191 }
192 }
193 return static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
194 }
195
196 //-----------------------------------------------------------------------------
197 // C Interface Routines
198 //-----------------------------------------------------------------------------
199 extern "C" {
200
clang_getNumDiagnostics(CXTranslationUnit Unit)201 unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) {
202 if (!Unit->TUData)
203 return 0;
204 return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics();
205 }
206
clang_getDiagnostic(CXTranslationUnit Unit,unsigned Index)207 CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) {
208 CXDiagnosticSet D = clang_getDiagnosticSetFromTU(Unit);
209 if (!D)
210 return 0;
211
212 CXDiagnosticSetImpl *Diags = static_cast<CXDiagnosticSetImpl*>(D);
213 if (Index >= Diags->getNumDiagnostics())
214 return 0;
215
216 return Diags->getDiagnostic(Index);
217 }
218
clang_getDiagnosticSetFromTU(CXTranslationUnit Unit)219 CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) {
220 if (!Unit->TUData)
221 return 0;
222 return static_cast<CXDiagnostic>(lazyCreateDiags(Unit));
223 }
224
clang_disposeDiagnostic(CXDiagnostic Diagnostic)225 void clang_disposeDiagnostic(CXDiagnostic Diagnostic) {
226 // No-op. Kept as a legacy API. CXDiagnostics are now managed
227 // by the enclosing CXDiagnosticSet.
228 }
229
clang_formatDiagnostic(CXDiagnostic Diagnostic,unsigned Options)230 CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) {
231 if (!Diagnostic)
232 return createCXString("");
233
234 CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic);
235
236 SmallString<256> Str;
237 llvm::raw_svector_ostream Out(Str);
238
239 if (Options & CXDiagnostic_DisplaySourceLocation) {
240 // Print source location (file:line), along with optional column
241 // and source ranges.
242 CXFile File;
243 unsigned Line, Column;
244 clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic),
245 &File, &Line, &Column, 0);
246 if (File) {
247 CXString FName = clang_getFileName(File);
248 Out << clang_getCString(FName) << ":" << Line << ":";
249 clang_disposeString(FName);
250 if (Options & CXDiagnostic_DisplayColumn)
251 Out << Column << ":";
252
253 if (Options & CXDiagnostic_DisplaySourceRanges) {
254 unsigned N = clang_getDiagnosticNumRanges(Diagnostic);
255 bool PrintedRange = false;
256 for (unsigned I = 0; I != N; ++I) {
257 CXFile StartFile, EndFile;
258 CXSourceRange Range = clang_getDiagnosticRange(Diagnostic, I);
259
260 unsigned StartLine, StartColumn, EndLine, EndColumn;
261 clang_getSpellingLocation(clang_getRangeStart(Range),
262 &StartFile, &StartLine, &StartColumn,
263 0);
264 clang_getSpellingLocation(clang_getRangeEnd(Range),
265 &EndFile, &EndLine, &EndColumn, 0);
266
267 if (StartFile != EndFile || StartFile != File)
268 continue;
269
270 Out << "{" << StartLine << ":" << StartColumn << "-"
271 << EndLine << ":" << EndColumn << "}";
272 PrintedRange = true;
273 }
274 if (PrintedRange)
275 Out << ":";
276 }
277
278 Out << " ";
279 }
280 }
281
282 /* Print warning/error/etc. */
283 switch (Severity) {
284 case CXDiagnostic_Ignored: llvm_unreachable("impossible");
285 case CXDiagnostic_Note: Out << "note: "; break;
286 case CXDiagnostic_Warning: Out << "warning: "; break;
287 case CXDiagnostic_Error: Out << "error: "; break;
288 case CXDiagnostic_Fatal: Out << "fatal error: "; break;
289 }
290
291 CXString Text = clang_getDiagnosticSpelling(Diagnostic);
292 if (clang_getCString(Text))
293 Out << clang_getCString(Text);
294 else
295 Out << "<no diagnostic text>";
296 clang_disposeString(Text);
297
298 if (Options & (CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId |
299 CXDiagnostic_DisplayCategoryName)) {
300 bool NeedBracket = true;
301 bool NeedComma = false;
302
303 if (Options & CXDiagnostic_DisplayOption) {
304 CXString OptionName = clang_getDiagnosticOption(Diagnostic, 0);
305 if (const char *OptionText = clang_getCString(OptionName)) {
306 if (OptionText[0]) {
307 Out << " [" << OptionText;
308 NeedBracket = false;
309 NeedComma = true;
310 }
311 }
312 clang_disposeString(OptionName);
313 }
314
315 if (Options & (CXDiagnostic_DisplayCategoryId |
316 CXDiagnostic_DisplayCategoryName)) {
317 if (unsigned CategoryID = clang_getDiagnosticCategory(Diagnostic)) {
318 if (Options & CXDiagnostic_DisplayCategoryId) {
319 if (NeedBracket)
320 Out << " [";
321 if (NeedComma)
322 Out << ", ";
323 Out << CategoryID;
324 NeedBracket = false;
325 NeedComma = true;
326 }
327
328 if (Options & CXDiagnostic_DisplayCategoryName) {
329 CXString CategoryName = clang_getDiagnosticCategoryText(Diagnostic);
330 if (NeedBracket)
331 Out << " [";
332 if (NeedComma)
333 Out << ", ";
334 Out << clang_getCString(CategoryName);
335 NeedBracket = false;
336 NeedComma = true;
337 clang_disposeString(CategoryName);
338 }
339 }
340 }
341
342 (void) NeedComma; // Silence dead store warning.
343 if (!NeedBracket)
344 Out << "]";
345 }
346
347 return createCXString(Out.str(), true);
348 }
349
clang_defaultDiagnosticDisplayOptions()350 unsigned clang_defaultDiagnosticDisplayOptions() {
351 return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn |
352 CXDiagnostic_DisplayOption;
353 }
354
clang_getDiagnosticSeverity(CXDiagnostic Diag)355 enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) {
356 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
357 return D->getSeverity();
358 return CXDiagnostic_Ignored;
359 }
360
clang_getDiagnosticLocation(CXDiagnostic Diag)361 CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) {
362 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
363 return D->getLocation();
364 return clang_getNullLocation();
365 }
366
clang_getDiagnosticSpelling(CXDiagnostic Diag)367 CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) {
368 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
369 return D->getSpelling();
370 return createCXString("");
371 }
372
clang_getDiagnosticOption(CXDiagnostic Diag,CXString * Disable)373 CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) {
374 if (Disable)
375 *Disable = createCXString("");
376
377 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
378 return D->getDiagnosticOption(Disable);
379
380 return createCXString("");
381 }
382
clang_getDiagnosticCategory(CXDiagnostic Diag)383 unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) {
384 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
385 return D->getCategory();
386 return 0;
387 }
388
clang_getDiagnosticCategoryName(unsigned Category)389 CXString clang_getDiagnosticCategoryName(unsigned Category) {
390 // Kept for backwards compatibility.
391 return createCXString(DiagnosticIDs::getCategoryNameFromID(Category));
392 }
393
clang_getDiagnosticCategoryText(CXDiagnostic Diag)394 CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) {
395 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
396 return D->getCategoryText();
397 return createCXString("");
398 }
399
clang_getDiagnosticNumRanges(CXDiagnostic Diag)400 unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) {
401 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
402 return D->getNumRanges();
403 return 0;
404 }
405
clang_getDiagnosticRange(CXDiagnostic Diag,unsigned Range)406 CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diag, unsigned Range) {
407 CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag);
408 if (!D || Range >= D->getNumRanges())
409 return clang_getNullRange();
410 return D->getRange(Range);
411 }
412
clang_getDiagnosticNumFixIts(CXDiagnostic Diag)413 unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) {
414 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
415 return D->getNumFixIts();
416 return 0;
417 }
418
clang_getDiagnosticFixIt(CXDiagnostic Diag,unsigned FixIt,CXSourceRange * ReplacementRange)419 CXString clang_getDiagnosticFixIt(CXDiagnostic Diag, unsigned FixIt,
420 CXSourceRange *ReplacementRange) {
421 CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag);
422 if (!D || FixIt >= D->getNumFixIts()) {
423 if (ReplacementRange)
424 *ReplacementRange = clang_getNullRange();
425 return createCXString("");
426 }
427 return D->getFixIt(FixIt, ReplacementRange);
428 }
429
clang_disposeDiagnosticSet(CXDiagnosticSet Diags)430 void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) {
431 CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags);
432 if (D->isExternallyManaged())
433 delete D;
434 }
435
clang_getDiagnosticInSet(CXDiagnosticSet Diags,unsigned Index)436 CXDiagnostic clang_getDiagnosticInSet(CXDiagnosticSet Diags,
437 unsigned Index) {
438 if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
439 if (Index < D->getNumDiagnostics())
440 return D->getDiagnostic(Index);
441 return 0;
442 }
443
clang_getChildDiagnostics(CXDiagnostic Diag)444 CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic Diag) {
445 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) {
446 CXDiagnosticSetImpl &ChildDiags = D->getChildDiagnostics();
447 return ChildDiags.empty() ? 0 : (CXDiagnosticSet) &ChildDiags;
448 }
449 return 0;
450 }
451
clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags)452 unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) {
453 if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
454 return D->getNumDiagnostics();
455 return 0;
456 }
457
458 } // end extern "C"
459