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