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 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 43 CXDiagnosticImpl::~CXDiagnosticImpl() {} 44 45 namespace { 46 class CXDiagnosticCustomNoteImpl : public CXDiagnosticImpl { 47 std::string Message; 48 CXSourceLocation Loc; 49 public: 50 CXDiagnosticCustomNoteImpl(StringRef Msg, CXSourceLocation L) 51 : CXDiagnosticImpl(CustomNoteDiagnosticKind), 52 Message(Msg), Loc(L) {} 53 54 virtual ~CXDiagnosticCustomNoteImpl() {} 55 56 CXDiagnosticSeverity getSeverity() const { 57 return CXDiagnostic_Note; 58 } 59 60 CXSourceLocation getLocation() const { 61 return Loc; 62 } 63 64 CXString getSpelling() const { 65 return createCXString(StringRef(Message), false); 66 } 67 68 CXString getDiagnosticOption(CXString *Disable) const { 69 if (Disable) 70 *Disable = createCXString("", false); 71 return createCXString("", false); 72 } 73 74 unsigned getCategory() const { return 0; } 75 CXString getCategoryText() const { return createCXString(""); } 76 77 unsigned getNumRanges() const { return 0; } 78 CXSourceRange getRange(unsigned Range) const { return clang_getNullRange(); } 79 unsigned getNumFixIts() const { return 0; } 80 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: 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 96 virtual ~CXDiagnosticRenderer() {} 97 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 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 128 virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, 129 DiagnosticsEngine::Level Level, 130 ArrayRef<CharSourceRange> Ranges) {} 131 132 virtual void emitCodeContext(SourceLocation Loc, 133 DiagnosticsEngine::Level Level, 134 SmallVectorImpl<CharSourceRange>& Ranges, 135 ArrayRef<FixItHint> Hints) {} 136 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 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 201 unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) { 202 if (!Unit->TUData) 203 return 0; 204 return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics(); 205 } 206 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 219 CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) { 220 if (!Unit->TUData) 221 return 0; 222 return static_cast<CXDiagnostic>(lazyCreateDiags(Unit)); 223 } 224 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 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 350 unsigned clang_defaultDiagnosticDisplayOptions() { 351 return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn | 352 CXDiagnostic_DisplayOption; 353 } 354 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 361 CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) { 362 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag)) 363 return D->getLocation(); 364 return clang_getNullLocation(); 365 } 366 367 CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) { 368 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) 369 return D->getSpelling(); 370 return createCXString(""); 371 } 372 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 383 unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) { 384 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) 385 return D->getCategory(); 386 return 0; 387 } 388 389 CXString clang_getDiagnosticCategoryName(unsigned Category) { 390 // Kept for backwards compatibility. 391 return createCXString(DiagnosticIDs::getCategoryNameFromID(Category)); 392 } 393 394 CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) { 395 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) 396 return D->getCategoryText(); 397 return createCXString(""); 398 } 399 400 unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) { 401 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) 402 return D->getNumRanges(); 403 return 0; 404 } 405 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 413 unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) { 414 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) 415 return D->getNumFixIts(); 416 return 0; 417 } 418 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 430 void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) { 431 CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags); 432 if (D->isExternallyManaged()) 433 delete D; 434 } 435 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 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 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