/// If \arg loc is a file ID and points inside the current macro /// definition, returns the appropriate source location pointing at the /// macro expansion source location entry, otherwise it returns an invalid /// SourceLocation. SourceLocation TokenLexer::getExpansionLocForMacroDefLoc(SourceLocation loc) const { assert(ExpandLocStart.isValid() && MacroExpansionStart.isValid() && "Not appropriate for token streams"); assert(loc.isValid() && loc.isFileID()); SourceManager &SM = PP.getSourceManager(); assert(SM.isInSLocAddrSpace(loc, MacroDefStart, MacroDefLength) && "Expected loc to come from the macro definition"); unsigned relativeOffset = 0; SM.isInSLocAddrSpace(loc, MacroDefStart, MacroDefLength, &relativeOffset); return MacroExpansionStart.getLocWithOffset(relativeOffset); }
// Checks if 'typedef' keyword can be removed - we do it only if // it is the only declaration in a declaration chain. static bool CheckRemoval(SourceManager &SM, SourceLocation StartLoc, ASTContext &Context) { assert(StartLoc.isFileID() && "StartLoc must not be in a macro"); std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(StartLoc); StringRef File = SM.getBufferData(LocInfo.first); const char *TokenBegin = File.data() + LocInfo.second; Lexer DeclLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(), File.begin(), TokenBegin, File.end()); Token Tok; int ParenLevel = 0; bool FoundTypedef = false; while (!DeclLexer.LexFromRawLexer(Tok) && !Tok.is(tok::semi)) { switch (Tok.getKind()) { case tok::l_brace: case tok::r_brace: // This might be the `typedef struct {...} T;` case. return false; case tok::l_paren: ParenLevel++; break; case tok::r_paren: ParenLevel--; break; case tok::comma: if (ParenLevel == 0) { // If there is comma and we are not between open parenthesis then it is // two or more declarations in this chain. return false; } break; case tok::raw_identifier: if (Tok.getRawIdentifier() == "typedef") { FoundTypedef = true; } break; default: break; } } // Sanity check against weird macro cases. return FoundTypedef; }
static bool isEmptyARCMTMacroStatement(NullStmt *S, std::vector<SourceLocation> &MacroLocs, ASTContext &Ctx) { if (!S->hasLeadingEmptyMacro()) return false; SourceLocation SemiLoc = S->getSemiLoc(); if (SemiLoc.isInvalid() || SemiLoc.isMacroID()) return false; if (MacroLocs.empty()) return false; SourceManager &SM = Ctx.getSourceManager(); std::vector<SourceLocation>::iterator I = std::upper_bound(MacroLocs.begin(), MacroLocs.end(), SemiLoc, BeforeThanCompare<SourceLocation>(SM)); --I; SourceLocation AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size()); assert(AfterMacroLoc.isFileID()); if (AfterMacroLoc == SemiLoc) return true; int RelOffs = 0; if (!SM.isInSameSLocAddrSpace(AfterMacroLoc, SemiLoc, &RelOffs)) return false; if (RelOffs < 0) return false; // We make the reasonable assumption that a semicolon after 100 characters // means that it is not the next token after our macro. If this assumption // fails it is not critical, we will just fail to clear out, e.g., an empty // 'if'. if (RelOffs - getARCMTMacroName().size() > 100) return false; SourceLocation AfterMacroSemiLoc = findSemiAfterLocation(AfterMacroLoc, Ctx); return AfterMacroSemiLoc == SemiLoc; }
static bool locationsInSameFile(const SourceManager &Sources, SourceLocation Loc1, SourceLocation Loc2) { return Loc1.isFileID() && Loc2.isFileID() && Sources.getFileID(Loc1) == Sources.getFileID(Loc2); }
CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const { SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID()); Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset()); assert(Loc.isFileID()); return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length)); }
SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const { SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID()); Loc = Loc.getLocWithOffset(Offset.getOffset()); assert(Loc.isFileID()); return Loc; }
/// \brief Recursively emit notes for each macro expansion and caret /// diagnostics where appropriate. /// /// Walks up the macro expansion stack printing expansion notes, the code /// snippet, caret, underlines and FixItHint display as appropriate at each /// level. /// /// \param Loc The location for this caret. /// \param Level The diagnostic level currently being emitted. /// \param Ranges The underlined ranges for this code snippet. /// \param Hints The FixIt hints active for this diagnostic. void DiagnosticRenderer::emitMacroExpansions(SourceLocation Loc, DiagnosticsEngine::Level Level, ArrayRef<CharSourceRange> Ranges, ArrayRef<FixItHint> Hints, const SourceManager &SM) { assert(!Loc.isInvalid() && "must have a valid source location here"); // Produce a stack of macro backtraces. SmallVector<SourceLocation, 8> LocationStack; unsigned IgnoredEnd = 0; while (Loc.isMacroID()) { // If this is the expansion of a macro argument, point the caret at the // use of the argument in the definition of the macro, not the expansion. if (SM.isMacroArgExpansion(Loc)) LocationStack.push_back(SM.getImmediateExpansionRange(Loc).first); else LocationStack.push_back(Loc); if (checkRangesForMacroArgExpansion(Loc, Ranges, SM)) IgnoredEnd = LocationStack.size(); Loc = SM.getImmediateMacroCallerLoc(Loc); // Once the location no longer points into a macro, try stepping through // the last found location. This sometimes produces additional useful // backtraces. if (Loc.isFileID()) Loc = SM.getImmediateMacroCallerLoc(LocationStack.back()); assert(!Loc.isInvalid() && "must have a valid source location here"); } LocationStack.erase(LocationStack.begin(), LocationStack.begin() + IgnoredEnd); unsigned MacroDepth = LocationStack.size(); unsigned MacroLimit = DiagOpts->MacroBacktraceLimit; if (MacroDepth <= MacroLimit || MacroLimit == 0) { for (auto I = LocationStack.rbegin(), E = LocationStack.rend(); I != E; ++I) emitSingleMacroExpansion(*I, Level, Ranges, SM); return; } unsigned MacroStartMessages = MacroLimit / 2; unsigned MacroEndMessages = MacroLimit / 2 + MacroLimit % 2; for (auto I = LocationStack.rbegin(), E = LocationStack.rbegin() + MacroStartMessages; I != E; ++I) emitSingleMacroExpansion(*I, Level, Ranges, SM); SmallString<200> MessageStorage; llvm::raw_svector_ostream Message(MessageStorage); Message << "(skipping " << (MacroDepth - MacroLimit) << " expansions in backtrace; use -fmacro-backtrace-limit=0 to " "see all)"; emitBasicNote(Message.str()); for (auto I = LocationStack.rend() - MacroEndMessages, E = LocationStack.rend(); I != E; ++I) emitSingleMacroExpansion(*I, Level, Ranges, SM); }
/// \brief Emit a code snippet and caret line. /// /// This routine emits a single line's code snippet and caret line.. /// /// \param Loc The location for the caret. /// \param Ranges The underlined ranges for this code snippet. /// \param Hints The FixIt hints active for this diagnostic. void TextDiagnostic::emitSnippetAndCaret( SourceLocation Loc, DiagnosticsEngine::Level Level, SmallVectorImpl<CharSourceRange>& Ranges, ArrayRef<FixItHint> Hints, const SourceManager &SM) { assert(!Loc.isInvalid() && "must have a valid source location here"); assert(Loc.isFileID() && "must have a file location here"); // If caret diagnostics are enabled and we have location, we want to // emit the caret. However, we only do this if the location moved // from the last diagnostic, if the last diagnostic was a note that // was part of a different warning or error diagnostic, or if the // diagnostic has ranges. We don't want to emit the same caret // multiple times if one loc has multiple diagnostics. if (!DiagOpts->ShowCarets) return; if (Loc == LastLoc && Ranges.empty() && Hints.empty() && (LastLevel != DiagnosticsEngine::Note || Level == LastLevel)) return; // Decompose the location into a FID/Offset pair. std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); FileID FID = LocInfo.first; unsigned FileOffset = LocInfo.second; // Get information about the buffer it points into. bool Invalid = false; const char *BufStart = SM.getBufferData(FID, &Invalid).data(); if (Invalid) return; unsigned LineNo = SM.getLineNumber(FID, FileOffset); unsigned ColNo = SM.getColumnNumber(FID, FileOffset); // Rewind from the current position to the start of the line. const char *TokPtr = BufStart+FileOffset; const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based. // Compute the line end. Scan forward from the error position to the end of // the line. const char *LineEnd = TokPtr; while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0') ++LineEnd; // Copy the line of code into an std::string for ease of manipulation. std::string SourceLine(LineStart, LineEnd); // Create a line for the caret that is filled with spaces that is the same // length as the line of source code. std::string CaretLine(LineEnd-LineStart, ' '); const SourceColumnMap sourceColMap(SourceLine, DiagOpts->TabStop); // Highlight all of the characters covered by Ranges with ~ characters. for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) highlightRange(*I, LineNo, FID, sourceColMap, CaretLine, SM, LangOpts); // Next, insert the caret itself. ColNo = sourceColMap.byteToContainingColumn(ColNo-1); if (CaretLine.size()<ColNo+1) CaretLine.resize(ColNo+1, ' '); CaretLine[ColNo] = '^'; std::string FixItInsertionLine = buildFixItInsertionLine(LineNo, sourceColMap, Hints, SM, DiagOpts.getPtr()); // If the source line is too long for our terminal, select only the // "interesting" source region within that line. unsigned Columns = DiagOpts->MessageLength; if (Columns) selectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine, Columns, sourceColMap); // If we are in -fdiagnostics-print-source-range-info mode, we are trying // to produce easily machine parsable output. Add a space before the // source line and the caret to make it trivial to tell the main diagnostic // line from what the user is intended to see. if (DiagOpts->ShowSourceRanges) { SourceLine = ' ' + SourceLine; CaretLine = ' ' + CaretLine; } // Finally, remove any blank spaces from the end of CaretLine. while (CaretLine[CaretLine.size()-1] == ' ') CaretLine.erase(CaretLine.end()-1); // Emit what we have computed. emitSnippet(SourceLine); if (DiagOpts->ShowColors) OS.changeColor(caretColor, true); OS << CaretLine << '\n'; if (DiagOpts->ShowColors) OS.resetColor(); if (!FixItInsertionLine.empty()) { if (DiagOpts->ShowColors) // Print fixit line in color OS.changeColor(fixitColor, false); if (DiagOpts->ShowSourceRanges) OS << ' '; OS << FixItInsertionLine << '\n'; if (DiagOpts->ShowColors) OS.resetColor(); } // Print out any parseable fixit information requested by the options. emitParseableFixits(Hints, SM); }
void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { llvm::OwningPtr<PathDiagnostic> OwningD(D); if (!D || D->path.empty()) return; // We need to flatten the locations (convert Stmt* to locations) because // the referenced statements may be freed by the time the diagnostics // are emitted. D->flattenLocations(); // If the PathDiagnosticConsumer does not support diagnostics that // cross file boundaries, prune out such diagnostics now. if (!supportsCrossFileDiagnostics()) { // Verify that the entire path is from the same FileID. FileID FID; const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager(); llvm::SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { const PathPieces &path = *WorkList.back(); WorkList.pop_back(); for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) { const PathDiagnosticPiece *piece = I->getPtr(); FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); if (FID.isInvalid()) { FID = SMgr.getFileID(L); } else if (SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? // Check the source ranges. for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), RE = piece->ranges_end(); RI != RE; ++RI) { SourceLocation L = SMgr.getExpansionLoc(RI->getBegin()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? L = SMgr.getExpansionLoc(RI->getEnd()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? } if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { WorkList.push_back(¯o->subPieces); } } } if (FID.isInvalid()) return; // FIXME: Emit a warning? } // Profile the node to see if we already have something matching it llvm::FoldingSetNodeID profile; D->Profile(profile); void *InsertPos = 0; if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { // Keep the PathDiagnostic with the shorter path. const unsigned orig_size = orig->full_size(); const unsigned new_size = D->full_size(); if (orig_size <= new_size) { bool shouldKeepOriginal = true; if (orig_size == new_size) { // Here we break ties in a fairly arbitrary, but deterministic, way. llvm::FoldingSetNodeID fullProfile, fullProfileOrig; D->FullProfile(fullProfile); orig->FullProfile(fullProfileOrig); if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash()) shouldKeepOriginal = false; } if (shouldKeepOriginal) return; } Diags.RemoveNode(orig); delete orig; } Diags.InsertNode(OwningD.take()); }
void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc, SourceRange *Ranges, unsigned NumRanges, SourceManager &SM, const CodeModificationHint *Hints, unsigned NumHints, unsigned Columns) { assert(LangOpts && "Unexpected diagnostic outside source file processing"); assert(!Loc.isInvalid() && "must have a valid source location here"); // If this is a macro ID, first emit information about where this was // instantiated (recursively) then emit information about where the token was // spelled from. if (!Loc.isFileID()) { SourceLocation OneLevelUp = SM.getImmediateInstantiationRange(Loc).first; // FIXME: Map ranges? EmitCaretDiagnostic(OneLevelUp, Ranges, NumRanges, SM, 0, 0, Columns); // Map the location. Loc = SM.getImmediateSpellingLoc(Loc); // Map the ranges. for (unsigned i = 0; i != NumRanges; ++i) { SourceLocation S = Ranges[i].getBegin(), E = Ranges[i].getEnd(); if (S.isMacroID()) S = SM.getImmediateSpellingLoc(S); if (E.isMacroID()) E = SM.getImmediateSpellingLoc(E); Ranges[i] = SourceRange(S, E); } // Get the pretty name, according to #line directives etc. PresumedLoc PLoc = SM.getPresumedLoc(Loc); // If this diagnostic is not in the main file, print out the "included from" // lines. if (LastWarningLoc != PLoc.getIncludeLoc()) { LastWarningLoc = PLoc.getIncludeLoc(); PrintIncludeStack(LastWarningLoc, SM); } if (DiagOpts->ShowLocation) { // Emit the file/line/column that this expansion came from. OS << PLoc.getFilename() << ':' << PLoc.getLine() << ':'; if (DiagOpts->ShowColumn) OS << PLoc.getColumn() << ':'; OS << ' '; } OS << "note: instantiated from:\n"; EmitCaretDiagnostic(Loc, Ranges, NumRanges, SM, Hints, NumHints, Columns); return; } // Decompose the location into a FID/Offset pair. std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); FileID FID = LocInfo.first; unsigned FileOffset = LocInfo.second; // Get information about the buffer it points into. std::pair<const char*, const char*> BufferInfo = SM.getBufferData(FID); const char *BufStart = BufferInfo.first; unsigned ColNo = SM.getColumnNumber(FID, FileOffset); unsigned CaretEndColNo = ColNo + Lexer::MeasureTokenLength(Loc, SM, *LangOpts); // Rewind from the current position to the start of the line. const char *TokPtr = BufStart+FileOffset; const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based. // Compute the line end. Scan forward from the error position to the end of // the line. const char *LineEnd = TokPtr; while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0') ++LineEnd; // FIXME: This shouldn't be necessary, but the CaretEndColNo can extend past // the source line length as currently being computed. See // test/Misc/message-length.c. CaretEndColNo = std::min(CaretEndColNo, unsigned(LineEnd - LineStart)); // Copy the line of code into an std::string for ease of manipulation. std::string SourceLine(LineStart, LineEnd); // Create a line for the caret that is filled with spaces that is the same // length as the line of source code. std::string CaretLine(LineEnd-LineStart, ' '); // Highlight all of the characters covered by Ranges with ~ characters. if (NumRanges) { unsigned LineNo = SM.getLineNumber(FID, FileOffset); for (unsigned i = 0, e = NumRanges; i != e; ++i) HighlightRange(Ranges[i], SM, LineNo, FID, CaretLine, SourceLine); } // Next, insert the caret itself. if (ColNo-1 < CaretLine.size()) CaretLine[ColNo-1] = '^'; else CaretLine.push_back('^'); // Scan the source line, looking for tabs. If we find any, manually expand // them to spaces and update the CaretLine to match. for (unsigned i = 0; i != SourceLine.size(); ++i) { if (SourceLine[i] != '\t') continue; // Replace this tab with at least one space. SourceLine[i] = ' '; // Compute the number of spaces we need to insert. unsigned TabStop = DiagOpts->TabStop; assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop && "Invalid -ftabstop value"); unsigned NumSpaces = ((i+TabStop)/TabStop * TabStop) - (i+1); assert(NumSpaces < TabStop && "Invalid computation of space amt"); // Insert spaces into the SourceLine. SourceLine.insert(i+1, NumSpaces, ' '); // Insert spaces or ~'s into CaretLine. CaretLine.insert(i+1, NumSpaces, CaretLine[i] == '~' ? '~' : ' '); } // If we are in -fdiagnostics-print-source-range-info mode, we are trying to // produce easily machine parsable output. Add a space before the source line // and the caret to make it trivial to tell the main diagnostic line from what // the user is intended to see. if (DiagOpts->ShowSourceRanges) { SourceLine = ' ' + SourceLine; CaretLine = ' ' + CaretLine; } std::string FixItInsertionLine; if (NumHints && DiagOpts->ShowFixits) { for (const CodeModificationHint *Hint = Hints, *LastHint = Hints + NumHints; Hint != LastHint; ++Hint) { if (Hint->InsertionLoc.isValid()) { // We have an insertion hint. Determine whether the inserted // code is on the same line as the caret. std::pair<FileID, unsigned> HintLocInfo = SM.getDecomposedInstantiationLoc(Hint->InsertionLoc); if (SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) == SM.getLineNumber(FID, FileOffset)) { // Insert the new code into the line just below the code // that the user wrote. unsigned HintColNo = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second); unsigned LastColumnModified = HintColNo - 1 + Hint->CodeToInsert.size(); if (LastColumnModified > FixItInsertionLine.size()) FixItInsertionLine.resize(LastColumnModified, ' '); std::copy(Hint->CodeToInsert.begin(), Hint->CodeToInsert.end(), FixItInsertionLine.begin() + HintColNo - 1); } else { FixItInsertionLine.clear(); break; } } } // Now that we have the entire fixit line, expand the tabs in it. // Since we don't want to insert spaces in the middle of a word, // find each word and the column it should line up with and insert // spaces until they match. if (!FixItInsertionLine.empty()) { unsigned FixItPos = 0; unsigned LinePos = 0; unsigned TabExpandedCol = 0; unsigned LineLength = LineEnd - LineStart; while (FixItPos < FixItInsertionLine.size() && LinePos < LineLength) { // Find the next word in the FixIt line. while (FixItPos < FixItInsertionLine.size() && FixItInsertionLine[FixItPos] == ' ') ++FixItPos; unsigned CharDistance = FixItPos - TabExpandedCol; // Walk forward in the source line, keeping track of // the tab-expanded column. for (unsigned I = 0; I < CharDistance; ++I, ++LinePos) if (LinePos >= LineLength || LineStart[LinePos] != '\t') ++TabExpandedCol; else TabExpandedCol = (TabExpandedCol/DiagOpts->TabStop + 1) * DiagOpts->TabStop; // Adjust the fixit line to match this column. FixItInsertionLine.insert(FixItPos, TabExpandedCol-FixItPos, ' '); FixItPos = TabExpandedCol; // Walk to the end of the word. while (FixItPos < FixItInsertionLine.size() && FixItInsertionLine[FixItPos] != ' ') ++FixItPos; } } } // If the source line is too long for our terminal, select only the // "interesting" source region within that line. if (Columns && SourceLine.size() > Columns) SelectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine, CaretEndColNo, Columns); // Finally, remove any blank spaces from the end of CaretLine. while (CaretLine[CaretLine.size()-1] == ' ') CaretLine.erase(CaretLine.end()-1); // Emit what we have computed. OS << SourceLine << '\n'; if (DiagOpts->ShowColors) OS.changeColor(caretColor, true); OS << CaretLine << '\n'; if (DiagOpts->ShowColors) OS.resetColor(); if (!FixItInsertionLine.empty()) { if (DiagOpts->ShowColors) // Print fixit line in color OS.changeColor(fixitColor, false); if (DiagOpts->ShowSourceRanges) OS << ' '; OS << FixItInsertionLine << '\n'; if (DiagOpts->ShowColors) OS.resetColor(); } }
void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, const DiagnosticInfo &Info) { // Default implementation (Warnings/errors count). DiagnosticClient::HandleDiagnostic(Level, Info); // Keeps track of the the starting position of the location // information (e.g., "foo.c:10:4:") that precedes the error // message. We use this information to determine how long the // file+line+column number prefix is. uint64_t StartOfLocationInfo = OS.tell(); if (!Prefix.empty()) OS << Prefix << ": "; // If the location is specified, print out a file/line/col and include trace // if enabled. if (Info.getLocation().isValid()) { const SourceManager &SM = Info.getSourceManager(); PresumedLoc PLoc = getDiagnosticPresumedLoc(SM, Info.getLocation()); if (PLoc.isInvalid()) { // At least print the file name if available: FileID FID = SM.getFileID(Info.getLocation()); if (!FID.isInvalid()) { const FileEntry* FE = SM.getFileEntryForID(FID); if (FE && FE->getName()) { OS << FE->getName(); if (FE->getDevice() == 0 && FE->getInode() == 0 && FE->getFileMode() == 0) { // in PCH is a guess, but a good one: OS << " (in PCH)"; } OS << ": "; } } } else { unsigned LineNo = PLoc.getLine(); // First, if this diagnostic is not in the main file, print out the // "included from" lines. if (LastWarningLoc != PLoc.getIncludeLoc()) { LastWarningLoc = PLoc.getIncludeLoc(); PrintIncludeStack(Level, LastWarningLoc, SM); StartOfLocationInfo = OS.tell(); } // Compute the column number. if (DiagOpts->ShowLocation) { if (DiagOpts->ShowColors) OS.changeColor(savedColor, true); OS << PLoc.getFilename(); switch (DiagOpts->Format) { case DiagnosticOptions::Clang: OS << ':' << LineNo; break; case DiagnosticOptions::Msvc: OS << '(' << LineNo; break; case DiagnosticOptions::Vi: OS << " +" << LineNo; break; } if (DiagOpts->ShowColumn) if (unsigned ColNo = PLoc.getColumn()) { if (DiagOpts->Format == DiagnosticOptions::Msvc) { OS << ','; ColNo--; } else OS << ':'; OS << ColNo; } switch (DiagOpts->Format) { case DiagnosticOptions::Clang: case DiagnosticOptions::Vi: OS << ':'; break; case DiagnosticOptions::Msvc: OS << ") : "; break; } if (DiagOpts->ShowSourceRanges && Info.getNumRanges()) { FileID CaretFileID = SM.getFileID(SM.getInstantiationLoc(Info.getLocation())); bool PrintedRange = false; for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) { // Ignore invalid ranges. if (!Info.getRange(i).isValid()) continue; SourceLocation B = Info.getRange(i).getBegin(); SourceLocation E = Info.getRange(i).getEnd(); B = SM.getInstantiationLoc(B); E = SM.getInstantiationLoc(E); // If the End location and the start location are the same and are a // macro location, then the range was something that came from a // macro expansion or _Pragma. If this is an object-like macro, the // best we can do is to highlight the range. If this is a // function-like macro, we'd also like to highlight the arguments. if (B == E && Info.getRange(i).getEnd().isMacroID()) E = SM.getInstantiationRange(Info.getRange(i).getEnd()).second; std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B); std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); // If the start or end of the range is in another file, just discard // it. if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) continue; // Add in the length of the token, so that we cover multi-char // tokens. unsigned TokSize = 0; if (Info.getRange(i).isTokenRange()) TokSize = Lexer::MeasureTokenLength(E, SM, *LangOpts); OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':' << SM.getColumnNumber(BInfo.first, BInfo.second) << '-' << SM.getLineNumber(EInfo.first, EInfo.second) << ':' << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) << '}'; PrintedRange = true; } if (PrintedRange) OS << ':'; } } OS << ' '; if (DiagOpts->ShowColors) OS.resetColor(); } } if (DiagOpts->ShowColors) { // Print diagnostic category in bold and color switch (Level) { case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); case Diagnostic::Note: OS.changeColor(noteColor, true); break; case Diagnostic::Warning: OS.changeColor(warningColor, true); break; case Diagnostic::Error: OS.changeColor(errorColor, true); break; case Diagnostic::Fatal: OS.changeColor(fatalColor, true); break; } } switch (Level) { case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); case Diagnostic::Note: OS << "note: "; break; case Diagnostic::Warning: OS << "warning: "; break; case Diagnostic::Error: OS << "error: "; break; case Diagnostic::Fatal: OS << "fatal error: "; break; } if (DiagOpts->ShowColors) OS.resetColor(); llvm::SmallString<100> OutStr; Info.FormatDiagnostic(OutStr); if (DiagOpts->ShowNames && !DiagnosticIDs::isBuiltinNote(Info.getID())) { OutStr += " ["; OutStr += DiagnosticIDs::getName(Info.getID()); OutStr += "]"; } std::string OptionName; if (DiagOpts->ShowOptionNames) { // Was this a warning mapped to an error using -Werror or pragma? if (Level == Diagnostic::Error && DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID())) { diag::Mapping mapping = diag::MAP_IGNORE; Info.getDiags()->getDiagnosticLevel(Info.getID(), Info.getLocation(), &mapping); if (mapping == diag::MAP_WARNING) OptionName += "-Werror"; } llvm::StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); if (!Opt.empty()) { if (!OptionName.empty()) OptionName += ','; OptionName += "-W"; OptionName += Opt; } else if (Info.getID() == diag::fatal_too_many_errors) { OptionName = "-ferror-limit="; } else { // If the diagnostic is an extension diagnostic and not enabled by default // then it must have been turned on with -pedantic. bool EnabledByDefault; if (DiagnosticIDs::isBuiltinExtensionDiag(Info.getID(), EnabledByDefault) && !EnabledByDefault) OptionName = "-pedantic"; } } // If the user wants to see category information, include it too. unsigned DiagCategory = 0; if (DiagOpts->ShowCategories) DiagCategory = DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); // If there is any categorization information, include it. if (!OptionName.empty() || DiagCategory != 0) { bool NeedsComma = false; OutStr += " ["; if (!OptionName.empty()) { OutStr += OptionName; NeedsComma = true; } if (DiagCategory) { if (NeedsComma) OutStr += ','; if (DiagOpts->ShowCategories == 1) OutStr += llvm::utostr(DiagCategory); else { assert(DiagOpts->ShowCategories == 2 && "Invalid ShowCategories value"); OutStr += DiagnosticIDs::getCategoryNameFromID(DiagCategory); } } OutStr += "]"; } if (DiagOpts->ShowColors) { // Print warnings, errors and fatal errors in bold, no color switch (Level) { case Diagnostic::Warning: OS.changeColor(savedColor, true); break; case Diagnostic::Error: OS.changeColor(savedColor, true); break; case Diagnostic::Fatal: OS.changeColor(savedColor, true); break; default: break; //don't bold notes } } if (DiagOpts->MessageLength) { // We will be word-wrapping the error message, so compute the // column number where we currently are (after printing the // location information). unsigned Column = OS.tell() - StartOfLocationInfo; PrintWordWrapped(OS, OutStr, DiagOpts->MessageLength, Column); } else { OS.write(OutStr.begin(), OutStr.size()); } OS << '\n'; if (DiagOpts->ShowColors) OS.resetColor(); // If caret diagnostics are enabled and we have location, we want to // emit the caret. However, we only do this if the location moved // from the last diagnostic, if the last diagnostic was a note that // was part of a different warning or error diagnostic, or if the // diagnostic has ranges. We don't want to emit the same caret // multiple times if one loc has multiple diagnostics. if (DiagOpts->ShowCarets && Info.getLocation().isValid() && ((LastLoc != Info.getLocation()) || Info.getNumRanges() || (LastCaretDiagnosticWasNote && Level != Diagnostic::Note) || Info.getNumFixItHints())) { // Cache the LastLoc, it allows us to omit duplicate source/caret spewage. LastLoc = FullSourceLoc(Info.getLocation(), Info.getSourceManager()); LastCaretDiagnosticWasNote = (Level == Diagnostic::Note); // Get the ranges into a local array we can hack on. CharSourceRange Ranges[20]; unsigned NumRanges = Info.getNumRanges(); assert(NumRanges < 20 && "Out of space"); for (unsigned i = 0; i != NumRanges; ++i) Ranges[i] = Info.getRange(i); unsigned NumHints = Info.getNumFixItHints(); for (unsigned i = 0; i != NumHints; ++i) { const FixItHint &Hint = Info.getFixItHint(i); if (Hint.RemoveRange.isValid()) { assert(NumRanges < 20 && "Out of space"); Ranges[NumRanges++] = Hint.RemoveRange; } } const SourceManager &SM = LastLoc.getManager(); unsigned MacroInstSkipStart = 0, MacroInstSkipEnd = 0; if (DiagOpts && DiagOpts->MacroBacktraceLimit && !LastLoc.isFileID()) { // Compute the length of the macro-expansion backtrace, so that we // can establish which steps in the macro backtrace we'll skip. SourceLocation Loc = LastLoc; unsigned Depth = 0; do { ++Depth; Loc = skipToMacroArgExpansion(SM, Loc); Loc = getImmediateMacroCallerLoc(SM, Loc); } while (!Loc.isFileID()); if (Depth > DiagOpts->MacroBacktraceLimit) { MacroInstSkipStart = DiagOpts->MacroBacktraceLimit / 2 + DiagOpts->MacroBacktraceLimit % 2; MacroInstSkipEnd = Depth - DiagOpts->MacroBacktraceLimit / 2; } } EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(), Info.getFixItHints(), Info.getNumFixItHints(), DiagOpts->MessageLength, 0, MacroInstSkipStart, MacroInstSkipEnd); } OS.flush(); }
void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc, SourceRange *Ranges, unsigned NumRanges, SourceManager &SM, const CodeModificationHint *Hints, unsigned NumHints, unsigned Columns) { assert(!Loc.isInvalid() && "must have a valid source location here"); // If this is a macro ID, first emit information about where this was // instantiated (recursively) then emit information about where. the token was // spelled from. if (!Loc.isFileID()) { SourceLocation OneLevelUp = SM.getImmediateInstantiationRange(Loc).first; // FIXME: Map ranges? EmitCaretDiagnostic(OneLevelUp, Ranges, NumRanges, SM, 0, 0, Columns); Loc = SM.getImmediateSpellingLoc(Loc); // Map the ranges. for (unsigned i = 0; i != NumRanges; ++i) { SourceLocation S = Ranges[i].getBegin(), E = Ranges[i].getEnd(); if (S.isMacroID()) S = SM.getImmediateSpellingLoc(S); if (E.isMacroID()) E = SM.getImmediateSpellingLoc(E); Ranges[i] = SourceRange(S, E); } if (ShowLocation) { std::pair<FileID, unsigned> IInfo = SM.getDecomposedInstantiationLoc(Loc); // Emit the file/line/column that this expansion came from. OS << SM.getBuffer(IInfo.first)->getBufferIdentifier() << ':' << SM.getLineNumber(IInfo.first, IInfo.second) << ':'; if (ShowColumn) OS << SM.getColumnNumber(IInfo.first, IInfo.second) << ':'; OS << ' '; } OS << "note: instantiated from:\n"; EmitCaretDiagnostic(Loc, Ranges, NumRanges, SM, Hints, NumHints, Columns); return; } // Decompose the location into a FID/Offset pair. std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); FileID FID = LocInfo.first; unsigned FileOffset = LocInfo.second; // Get information about the buffer it points into. std::pair<const char*, const char*> BufferInfo = SM.getBufferData(FID); const char *BufStart = BufferInfo.first; unsigned ColNo = SM.getColumnNumber(FID, FileOffset); unsigned CaretEndColNo = ColNo + Lexer::MeasureTokenLength(Loc, SM, *LangOpts); // Rewind from the current position to the start of the line. const char *TokPtr = BufStart+FileOffset; const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based. // Compute the line end. Scan forward from the error position to the end of // the line. const char *LineEnd = TokPtr; while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0') ++LineEnd; // Copy the line of code into an std::string for ease of manipulation. std::string SourceLine(LineStart, LineEnd); // Create a line for the caret that is filled with spaces that is the same // length as the line of source code. std::string CaretLine(LineEnd-LineStart, ' '); // Highlight all of the characters covered by Ranges with ~ characters. if (NumRanges) { unsigned LineNo = SM.getLineNumber(FID, FileOffset); for (unsigned i = 0, e = NumRanges; i != e; ++i) HighlightRange(Ranges[i], SM, LineNo, FID, CaretLine, SourceLine); } // Next, insert the caret itself. if (ColNo-1 < CaretLine.size()) CaretLine[ColNo-1] = '^'; else CaretLine.push_back('^'); // Scan the source line, looking for tabs. If we find any, manually expand // them to 8 characters and update the CaretLine to match. for (unsigned i = 0; i != SourceLine.size(); ++i) { if (SourceLine[i] != '\t') continue; // Replace this tab with at least one space. SourceLine[i] = ' '; // Compute the number of spaces we need to insert. unsigned NumSpaces = ((i+8)&~7) - (i+1); assert(NumSpaces < 8 && "Invalid computation of space amt"); // Insert spaces into the SourceLine. SourceLine.insert(i+1, NumSpaces, ' '); // Insert spaces or ~'s into CaretLine. CaretLine.insert(i+1, NumSpaces, CaretLine[i] == '~' ? '~' : ' '); } // If we are in -fdiagnostics-print-source-range-info mode, we are trying to // produce easily machine parsable output. Add a space before the source line // and the caret to make it trivial to tell the main diagnostic line from what // the user is intended to see. if (PrintRangeInfo) { SourceLine = ' ' + SourceLine; CaretLine = ' ' + CaretLine; } std::string FixItInsertionLine; if (NumHints && PrintFixItInfo) { for (const CodeModificationHint *Hint = Hints, *LastHint = Hints + NumHints; Hint != LastHint; ++Hint) { if (Hint->InsertionLoc.isValid()) { // We have an insertion hint. Determine whether the inserted // code is on the same line as the caret. std::pair<FileID, unsigned> HintLocInfo = SM.getDecomposedInstantiationLoc(Hint->InsertionLoc); if (SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) == SM.getLineNumber(FID, FileOffset)) { // Insert the new code into the line just below the code // that the user wrote. unsigned HintColNo = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second); unsigned LastColumnModified = HintColNo - 1 + Hint->CodeToInsert.size(); if (LastColumnModified > FixItInsertionLine.size()) FixItInsertionLine.resize(LastColumnModified, ' '); std::copy(Hint->CodeToInsert.begin(), Hint->CodeToInsert.end(), FixItInsertionLine.begin() + HintColNo - 1); } else { FixItInsertionLine.clear(); break; } } } } // If the source line is too long for our terminal, select only the // "interesting" source region within that line. if (Columns && SourceLine.size() > Columns) SelectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine, CaretEndColNo, Columns); // Finally, remove any blank spaces from the end of CaretLine. while (CaretLine[CaretLine.size()-1] == ' ') CaretLine.erase(CaretLine.end()-1); // Emit what we have computed. OS << SourceLine << '\n'; if (UseColors) OS.changeColor(caretColor, true); OS << CaretLine << '\n'; if (UseColors) OS.resetColor(); if (!FixItInsertionLine.empty()) { if (UseColors) // Print fixit line in color OS.changeColor(fixitColor, false); if (PrintRangeInfo) OS << ' '; OS << FixItInsertionLine << '\n'; if (UseColors) OS.resetColor(); } }
/// \brief Recursively emit notes for each macro expansion and caret /// diagnostics where appropriate. /// /// Walks up the macro expansion stack printing expansion notes, the code /// snippet, caret, underlines and FixItHint display as appropriate at each /// level. /// /// \param Loc The location for this caret. /// \param Level The diagnostic level currently being emitted. /// \param Ranges The underlined ranges for this code snippet. /// \param Hints The FixIt hints active for this diagnostic. /// \param MacroSkipEnd The depth to stop skipping macro expansions. /// \param OnMacroInst The current depth of the macro expansion stack. void DiagnosticRenderer::emitMacroExpansionsAndCarets( SourceLocation Loc, DiagnosticsEngine::Level Level, SmallVectorImpl<CharSourceRange>& Ranges, ArrayRef<FixItHint> Hints, const SourceManager &SM, unsigned &MacroDepth, unsigned OnMacroInst) { assert(!Loc.isInvalid() && "must have a valid source location here"); // If this is a file source location, directly emit the source snippet and // caret line. Also record the macro depth reached. if (Loc.isFileID()) { // Map the ranges. SmallVector<CharSourceRange, 4> SpellingRanges; mapDiagnosticRanges(Loc, Ranges, SpellingRanges, &SM); assert(MacroDepth == 0 && "We shouldn't hit a leaf node twice!"); MacroDepth = OnMacroInst; emitCodeContext(Loc, Level, SpellingRanges, Hints, SM); return; } // Otherwise recurse through each macro expansion layer. // When processing macros, skip over the expansions leading up to // a macro argument, and trace the argument's expansion stack instead. Loc = SM.skipToMacroArgExpansion(Loc); SourceLocation OneLevelUp = SM.getImmediateMacroCallerLoc(Loc); emitMacroExpansionsAndCarets(OneLevelUp, Level, Ranges, Hints, SM, MacroDepth, OnMacroInst + 1); // Save the original location so we can find the spelling of the macro call. SourceLocation MacroLoc = Loc; // Map the location. Loc = SM.getImmediateMacroCalleeLoc(Loc); unsigned MacroSkipStart = 0, MacroSkipEnd = 0; if (MacroDepth > DiagOpts->MacroBacktraceLimit && DiagOpts->MacroBacktraceLimit != 0) { MacroSkipStart = DiagOpts->MacroBacktraceLimit / 2 + DiagOpts->MacroBacktraceLimit % 2; MacroSkipEnd = MacroDepth - DiagOpts->MacroBacktraceLimit / 2; } // Whether to suppress printing this macro expansion. bool Suppressed = (OnMacroInst >= MacroSkipStart && OnMacroInst < MacroSkipEnd); if (Suppressed) { // Tell the user that we've skipped contexts. if (OnMacroInst == MacroSkipStart) { SmallString<200> MessageStorage; llvm::raw_svector_ostream Message(MessageStorage); Message << "(skipping " << (MacroSkipEnd - MacroSkipStart) << " expansions in backtrace; use -fmacro-backtrace-limit=0 to " "see all)"; emitBasicNote(Message.str()); } return; } // Map the ranges. SmallVector<CharSourceRange, 4> SpellingRanges; mapDiagnosticRanges(MacroLoc, Ranges, SpellingRanges, &SM); SmallString<100> MessageStorage; llvm::raw_svector_ostream Message(MessageStorage); Message << "expanded from macro '" << getImmediateMacroName(MacroLoc, SM, LangOpts) << "'"; emitDiagnostic(SM.getSpellingLoc(Loc), DiagnosticsEngine::Note, Message.str(), SpellingRanges, ArrayRef<FixItHint>(), &SM); }
/// \brief Recursively emit notes for each macro expansion and caret /// diagnostics where appropriate. /// /// Walks up the macro expansion stack printing expansion notes, the code /// snippet, caret, underlines and FixItHint display as appropriate at each /// level. /// /// \param Loc The location for this caret. /// \param Level The diagnostic level currently being emitted. /// \param Ranges The underlined ranges for this code snippet. /// \param Hints The FixIt hints active for this diagnostic. /// \param MacroSkipEnd The depth to stop skipping macro expansions. /// \param OnMacroInst The current depth of the macro expansion stack. void TextDiagnostic::emitMacroExpansionsAndCarets( SourceLocation Loc, DiagnosticsEngine::Level Level, SmallVectorImpl<CharSourceRange>& Ranges, ArrayRef<FixItHint> Hints, unsigned &MacroDepth, unsigned OnMacroInst) { assert(!Loc.isInvalid() && "must have a valid source location here"); // If this is a file source location, directly emit the source snippet and // caret line. Also record the macro depth reached. if (Loc.isFileID()) { assert(MacroDepth == 0 && "We shouldn't hit a leaf node twice!"); MacroDepth = OnMacroInst; emitSnippetAndCaret(Loc, Level, Ranges, Hints); return; } // Otherwise recurse through each macro expansion layer. // When processing macros, skip over the expansions leading up to // a macro argument, and trace the argument's expansion stack instead. Loc = skipToMacroArgExpansion(SM, Loc); SourceLocation OneLevelUp = getImmediateMacroCallerLoc(SM, Loc); // FIXME: Map ranges? emitMacroExpansionsAndCarets(OneLevelUp, Level, Ranges, Hints, MacroDepth, OnMacroInst + 1); // Save the original location so we can find the spelling of the macro call. SourceLocation MacroLoc = Loc; // Map the location. Loc = getImmediateMacroCalleeLoc(SM, Loc); unsigned MacroSkipStart = 0, MacroSkipEnd = 0; if (MacroDepth > DiagOpts.MacroBacktraceLimit) { MacroSkipStart = DiagOpts.MacroBacktraceLimit / 2 + DiagOpts.MacroBacktraceLimit % 2; MacroSkipEnd = MacroDepth - DiagOpts.MacroBacktraceLimit / 2; } // Whether to suppress printing this macro expansion. bool Suppressed = (OnMacroInst >= MacroSkipStart && OnMacroInst < MacroSkipEnd); // Map the ranges. for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { SourceLocation Start = I->getBegin(), End = I->getEnd(); if (Start.isMacroID()) I->setBegin(getImmediateMacroCalleeLoc(SM, Start)); if (End.isMacroID()) I->setEnd(getImmediateMacroCalleeLoc(SM, End)); } if (Suppressed) { // Tell the user that we've skipped contexts. if (OnMacroInst == MacroSkipStart) { // FIXME: Emit this as a real note diagnostic. // FIXME: Format an actual diagnostic rather than a hard coded string. OS << "note: (skipping " << (MacroSkipEnd - MacroSkipStart) << " expansions in backtrace; use -fmacro-backtrace-limit=0 to see " "all)\n"; } return; } llvm::SmallString<100> MessageStorage; llvm::raw_svector_ostream Message(MessageStorage); Message << "expanded from macro '" << getImmediateMacroName(MacroLoc, SM, LangOpts) << "'"; emitDiagnostic(SM.getSpellingLoc(Loc), DiagnosticsEngine::Note, Message.str(), Ranges, ArrayRef<FixItHint>()); }
void PathDiagnosticConsumer::HandlePathDiagnostic( std::unique_ptr<PathDiagnostic> D) { if (!D || D->path.empty()) return; // We need to flatten the locations (convert Stmt* to locations) because // the referenced statements may be freed by the time the diagnostics // are emitted. D->flattenLocations(); // If the PathDiagnosticConsumer does not support diagnostics that // cross file boundaries, prune out such diagnostics now. if (!supportsCrossFileDiagnostics()) { // Verify that the entire path is from the same FileID. FileID FID; const SourceManager &SMgr = D->path.front()->getLocation().getManager(); SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); SmallString<128> buf; llvm::raw_svector_ostream warning(buf); warning << "warning: Path diagnostic report is not generated. Current " << "output format does not support diagnostics that cross file " << "boundaries. Refer to --analyzer-output for valid output " << "formats\n"; while (!WorkList.empty()) { const PathPieces &path = *WorkList.pop_back_val(); for (const auto &I : path) { const PathDiagnosticPiece *piece = I.get(); FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); if (FID.isInvalid()) { FID = SMgr.getFileID(L); } else if (SMgr.getFileID(L) != FID) { llvm::errs() << warning.str(); return; } // Check the source ranges. ArrayRef<SourceRange> Ranges = piece->getRanges(); for (const auto &I : Ranges) { SourceLocation L = SMgr.getExpansionLoc(I.getBegin()); if (!L.isFileID() || SMgr.getFileID(L) != FID) { llvm::errs() << warning.str(); return; } L = SMgr.getExpansionLoc(I.getEnd()); if (!L.isFileID() || SMgr.getFileID(L) != FID) { llvm::errs() << warning.str(); return; } } if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece)) WorkList.push_back(&call->path); else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) WorkList.push_back(¯o->subPieces); } } if (FID.isInvalid()) return; // FIXME: Emit a warning? } // Profile the node to see if we already have something matching it llvm::FoldingSetNodeID profile; D->Profile(profile); void *InsertPos = nullptr; if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { // Keep the PathDiagnostic with the shorter path. // Note, the enclosing routine is called in deterministic order, so the // results will be consistent between runs (no reason to break ties if the // size is the same). const unsigned orig_size = orig->full_size(); const unsigned new_size = D->full_size(); if (orig_size <= new_size) return; assert(orig != D.get()); Diags.RemoveNode(orig); delete orig; } Diags.InsertNode(D.release()); }
/// PasteTokens - Tok is the LHS of a ## operator, and CurToken is the ## /// operator. Read the ## and RHS, and paste the LHS/RHS together. If there /// are more ## after it, chomp them iteratively. Return the result as Tok. /// If this returns true, the caller should immediately return the token. bool TokenLexer::PasteTokens(Token &Tok) { SmallString<128> Buffer; const char *ResultTokStrPtr = 0; SourceLocation StartLoc = Tok.getLocation(); SourceLocation PasteOpLoc; do { // Consume the ## operator. PasteOpLoc = Tokens[CurToken].getLocation(); ++CurToken; assert(!isAtEnd() && "No token on the RHS of a paste operator!"); // Get the RHS token. const Token &RHS = Tokens[CurToken]; // Allocate space for the result token. This is guaranteed to be enough for // the two tokens. Buffer.resize(Tok.getLength() + RHS.getLength()); // Get the spelling of the LHS token in Buffer. const char *BufPtr = &Buffer[0]; bool Invalid = false; unsigned LHSLen = PP.getSpelling(Tok, BufPtr, &Invalid); if (BufPtr != &Buffer[0]) // Really, we want the chars in Buffer! memcpy(&Buffer[0], BufPtr, LHSLen); if (Invalid) return true; BufPtr = &Buffer[LHSLen]; unsigned RHSLen = PP.getSpelling(RHS, BufPtr, &Invalid); if (Invalid) return true; if (BufPtr != &Buffer[LHSLen]) // Really, we want the chars in Buffer! memcpy(&Buffer[LHSLen], BufPtr, RHSLen); // Trim excess space. Buffer.resize(LHSLen+RHSLen); // Plop the pasted result (including the trailing newline and null) into a // scratch buffer where we can lex it. Token ResultTokTmp; ResultTokTmp.startToken(); // Claim that the tmp token is a string_literal so that we can get the // character pointer back from CreateString in getLiteralData(). ResultTokTmp.setKind(tok::string_literal); PP.CreateString(&Buffer[0], Buffer.size(), ResultTokTmp); SourceLocation ResultTokLoc = ResultTokTmp.getLocation(); ResultTokStrPtr = ResultTokTmp.getLiteralData(); // Lex the resultant pasted token into Result. Token Result; if (Tok.isAnyIdentifier() && RHS.isAnyIdentifier()) { // Common paste case: identifier+identifier = identifier. Avoid creating // a lexer and other overhead. PP.IncrementPasteCounter(true); Result.startToken(); Result.setKind(tok::raw_identifier); Result.setRawIdentifierData(ResultTokStrPtr); Result.setLocation(ResultTokLoc); Result.setLength(LHSLen+RHSLen); } else { PP.IncrementPasteCounter(false); assert(ResultTokLoc.isFileID() && "Should be a raw location into scratch buffer"); SourceManager &SourceMgr = PP.getSourceManager(); FileID LocFileID = SourceMgr.getFileID(ResultTokLoc); bool Invalid = false; const char *ScratchBufStart = SourceMgr.getBufferData(LocFileID, &Invalid).data(); if (Invalid) return false; // Make a lexer to lex this string from. Lex just this one token. // Make a lexer object so that we lex and expand the paste result. Lexer TL(SourceMgr.getLocForStartOfFile(LocFileID), PP.getLangOpts(), ScratchBufStart, ResultTokStrPtr, ResultTokStrPtr+LHSLen+RHSLen); // Lex a token in raw mode. This way it won't look up identifiers // automatically, lexing off the end will return an eof token, and // warnings are disabled. This returns true if the result token is the // entire buffer. bool isInvalid = !TL.LexFromRawLexer(Result); // If we got an EOF token, we didn't form even ONE token. For example, we // did "/ ## /" to get "//". isInvalid |= Result.is(tok::eof); // If pasting the two tokens didn't form a full new token, this is an // error. This occurs with "x ## +" and other stuff. Return with Tok // unmodified and with RHS as the next token to lex. if (isInvalid) { // Test for the Microsoft extension of /##/ turning into // here on the // error path. if (PP.getLangOpts().MicrosoftExt && Tok.is(tok::slash) && RHS.is(tok::slash)) { HandleMicrosoftCommentPaste(Tok); return true; } // Do not emit the error when preprocessing assembler code. if (!PP.getLangOpts().AsmPreprocessor) { // Explicitly convert the token location to have proper expansion // information so that the user knows where it came from. SourceManager &SM = PP.getSourceManager(); SourceLocation Loc = SM.createExpansionLoc(PasteOpLoc, ExpandLocStart, ExpandLocEnd, 2); // If we're in microsoft extensions mode, downgrade this from a hard // error to a warning that defaults to an error. This allows // disabling it. PP.Diag(Loc, PP.getLangOpts().MicrosoftExt ? diag::err_pp_bad_paste_ms : diag::err_pp_bad_paste) << Buffer.str(); } // An error has occurred so exit loop. break; } // Turn ## into 'unknown' to avoid # ## # from looking like a paste // operator. if (Result.is(tok::hashhash)) Result.setKind(tok::unknown); } // Transfer properties of the LHS over the the Result. Result.setFlagValue(Token::StartOfLine , Tok.isAtStartOfLine()); Result.setFlagValue(Token::LeadingSpace, Tok.hasLeadingSpace()); // Finally, replace LHS with the result, consume the RHS, and iterate. ++CurToken; Tok = Result; } while (!isAtEnd() && Tokens[CurToken].is(tok::hashhash)); SourceLocation EndLoc = Tokens[CurToken - 1].getLocation(); // The token's current location indicate where the token was lexed from. We // need this information to compute the spelling of the token, but any // diagnostics for the expanded token should appear as if the token was // expanded from the full ## expression. Pull this information together into // a new SourceLocation that captures all of this. SourceManager &SM = PP.getSourceManager(); if (StartLoc.isFileID()) StartLoc = getExpansionLocForMacroDefLoc(StartLoc); if (EndLoc.isFileID()) EndLoc = getExpansionLocForMacroDefLoc(EndLoc); Tok.setLocation(SM.createExpansionLoc(Tok.getLocation(), StartLoc, EndLoc, Tok.getLength())); // Now that we got the result token, it will be subject to expansion. Since // token pasting re-lexes the result token in raw mode, identifier information // isn't looked up. As such, if the result is an identifier, look up id info. if (Tok.is(tok::raw_identifier)) { // Look up the identifier info for the token. We disabled identifier lookup // by saying we're skipping contents, so we need to do this manually. PP.LookUpIdentifierInfo(Tok); } return false; }
void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { llvm::OwningPtr<PathDiagnostic> OwningD(D); if (!D || D->path.empty()) return; // We need to flatten the locations (convert Stmt* to locations) because // the referenced statements may be freed by the time the diagnostics // are emitted. D->flattenLocations(); // If the PathDiagnosticConsumer does not support diagnostics that // cross file boundaries, prune out such diagnostics now. if (!supportsCrossFileDiagnostics()) { // Verify that the entire path is from the same FileID. FileID FID; const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager(); llvm::SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { const PathPieces &path = *WorkList.back(); WorkList.pop_back(); for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) { const PathDiagnosticPiece *piece = I->getPtr(); FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); if (FID.isInvalid()) { FID = SMgr.getFileID(L); } else if (SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? // Check the source ranges. ArrayRef<SourceRange> Ranges = piece->getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { SourceLocation L = SMgr.getExpansionLoc(I->getBegin()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? L = SMgr.getExpansionLoc(I->getEnd()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? } if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { WorkList.push_back(¯o->subPieces); } } } if (FID.isInvalid()) return; // FIXME: Emit a warning? } // Profile the node to see if we already have something matching it llvm::FoldingSetNodeID profile; D->Profile(profile); void *InsertPos = 0; if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { // Keep the PathDiagnostic with the shorter path. // Note, the enclosing routine is called in deterministic order, so the // results will be consistent between runs (no reason to break ties if the // size is the same). const unsigned orig_size = orig->full_size(); const unsigned new_size = D->full_size(); if (orig_size <= new_size) return; assert(orig != D); Diags.RemoveNode(orig); delete orig; } Diags.InsertNode(OwningD.take()); }
bool Preprocessor::isCodeCompletionFile(SourceLocation FileLoc) const { return CodeCompletionFile && FileLoc.isFileID() && SourceMgr.getFileEntryForID(SourceMgr.getFileID(FileLoc)) == CodeCompletionFile; }