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