/// 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);
}
Exemple #5
0
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));
}
Exemple #6
0
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);
}
Exemple #9
0
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(&macro->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();
  }
}
Exemple #11
0
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();
  }
}
Exemple #13
0
/// \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);
}
Exemple #14
0
/// \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(&macro->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());
}
Exemple #16
0
/// 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;
}
Exemple #17
0
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(&macro->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());
}
Exemple #18
0
bool Preprocessor::isCodeCompletionFile(SourceLocation FileLoc) const {
  return CodeCompletionFile && FileLoc.isFileID() &&
    SourceMgr.getFileEntryForID(SourceMgr.getFileID(FileLoc))
      == CodeCompletionFile;
}