예제 #1
0
std::string TextDiagnostic::buildFixItInsertionLine(
  unsigned LineNo,
  const SourceColumnMap &map,
  ArrayRef<FixItHint> Hints) {

  std::string FixItInsertionLine;
  if (Hints.empty() || !DiagOpts.ShowFixits)
    return FixItInsertionLine;

  for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
       I != E; ++I) {
    if (!I->CodeToInsert.empty()) {
      // We have an insertion hint. Determine whether the inserted
      // code is on the same line as the caret.
      std::pair<FileID, unsigned> HintLocInfo
        = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin());
      if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second)) {
        // Insert the new code into the line just below the code
        // that the user wrote.
        unsigned HintColNo
          = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1;
        // hint must start inside the source or right at the end
        assert(HintColNo<static_cast<unsigned>(map.bytes())+1);
        HintColNo = map.byteToColumn(HintColNo);

        // FIXME: if the fixit includes tabs or other characters that do not
        //  take up a single column per byte when displayed then
        //  I->CodeToInsert.size() is not a column number and we're mixing
        //  units (columns + bytes). We should get printable versions
        //  of each fixit before using them.
        unsigned LastColumnModified
          = HintColNo + I->CodeToInsert.size();

        if (LastColumnModified > static_cast<unsigned>(map.bytes())) {
          unsigned LastExistingColumn = map.byteToColumn(map.bytes());
          unsigned AddedColumns = LastColumnModified-LastExistingColumn;
          LastColumnModified = LastExistingColumn + AddedColumns;
        } else {
          LastColumnModified = map.byteToColumn(LastColumnModified);
        }

        if (LastColumnModified > FixItInsertionLine.size())
          FixItInsertionLine.resize(LastColumnModified, ' ');
        assert(HintColNo+I->CodeToInsert.size() <= FixItInsertionLine.size());
        std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(),
                  FixItInsertionLine.begin() + HintColNo);
      } else {
        FixItInsertionLine.clear();
        break;
      }
    }
  }

  expandTabs(FixItInsertionLine, DiagOpts.TabStop);

  return FixItInsertionLine;
}
예제 #2
0
static std::string buildFixItInsertionLine(unsigned LineNo,
                                           const SourceColumnMap &map,
                                           ArrayRef<FixItHint> Hints,
                                           const SourceManager &SM,
                                           const DiagnosticOptions *DiagOpts) {
  std::string FixItInsertionLine;
  if (Hints.empty() || !DiagOpts->ShowFixits)
    return FixItInsertionLine;
  unsigned PrevHintEndCol = 0;

  for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
       I != E; ++I) {
    if (!I->CodeToInsert.empty()) {
      // We have an insertion hint. Determine whether the inserted
      // code contains no newlines and is on the same line as the caret.
      std::pair<FileID, unsigned> HintLocInfo
        = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin());
      if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) &&
          StringRef(I->CodeToInsert).find_first_of("\n\r") == StringRef::npos) {
        // Insert the new code into the line just below the code
        // that the user wrote.
        // Note: When modifying this function, be very careful about what is a
        // "column" (printed width, platform-dependent) and what is a
        // "byte offset" (SourceManager "column").
        unsigned HintByteOffset
          = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1;

        // The hint must start inside the source or right at the end
        assert(HintByteOffset < static_cast<unsigned>(map.bytes())+1);
        unsigned HintCol = map.byteToContainingColumn(HintByteOffset);

        // If we inserted a long previous hint, push this one forwards, and add
        // an extra space to show that this is not part of the previous
        // completion. This is sort of the best we can do when two hints appear
        // to overlap.
        //
        // Note that if this hint is located immediately after the previous
        // hint, no space will be added, since the location is more important.
        if (HintCol < PrevHintEndCol)
          HintCol = PrevHintEndCol + 1;

        // FIXME: This function handles multibyte characters in the source, but
        // not in the fixits. This assertion is intended to catch unintended
        // use of multibyte characters in fixits. If we decide to do this, we'll
        // have to track separate byte widths for the source and fixit lines.
        assert((size_t)llvm::sys::locale::columnWidth(I->CodeToInsert) ==
               I->CodeToInsert.size());

        // This relies on one byte per column in our fixit hints.
        // This should NOT use HintByteOffset, because the source might have
        // Unicode characters in earlier columns.
        unsigned LastColumnModified = HintCol + I->CodeToInsert.size();
        if (LastColumnModified > FixItInsertionLine.size())
          FixItInsertionLine.resize(LastColumnModified, ' ');

        std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(),
                  FixItInsertionLine.begin() + HintCol);

        PrevHintEndCol = LastColumnModified;
      } else {
        FixItInsertionLine.clear();
        break;
      }
    }
  }

  expandTabs(FixItInsertionLine, DiagOpts->TabStop);

  return FixItInsertionLine;
}
예제 #3
0
/// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo.
static void highlightRange(const CharSourceRange &R,
                           unsigned LineNo, FileID FID,
                           const SourceColumnMap &map,
                           std::string &CaretLine,
                           const SourceManager &SM,
                           const LangOptions &LangOpts) {
  if (!R.isValid()) return;

  SourceLocation Begin = R.getBegin();
  SourceLocation End = R.getEnd();

  unsigned StartLineNo = SM.getExpansionLineNumber(Begin);
  if (StartLineNo > LineNo || SM.getFileID(Begin) != FID)
    return;  // No intersection.

  unsigned EndLineNo = SM.getExpansionLineNumber(End);
  if (EndLineNo < LineNo || SM.getFileID(End) != FID)
    return;  // No intersection.

  // Compute the column number of the start.
  unsigned StartColNo = 0;
  if (StartLineNo == LineNo) {
    StartColNo = SM.getExpansionColumnNumber(Begin);
    if (StartColNo) --StartColNo;  // Zero base the col #.
  }

  // Compute the column number of the end.
  unsigned EndColNo = map.getSourceLine().size();
  if (EndLineNo == LineNo) {
    EndColNo = SM.getExpansionColumnNumber(End);
    if (EndColNo) {
      --EndColNo;  // Zero base the col #.

      // Add in the length of the token, so that we cover multi-char tokens if
      // this is a token range.
      if (R.isTokenRange())
        EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts);
    } else {
      EndColNo = CaretLine.size();
    }
  }

  assert(StartColNo <= EndColNo && "Invalid range!");

  // Check that a token range does not highlight only whitespace.
  if (R.isTokenRange()) {
    // Pick the first non-whitespace column.
    while (StartColNo < map.getSourceLine().size() &&
           (map.getSourceLine()[StartColNo] == ' ' ||
            map.getSourceLine()[StartColNo] == '\t'))
      StartColNo = map.startOfNextColumn(StartColNo);

    // Pick the last non-whitespace column.
    if (EndColNo > map.getSourceLine().size())
      EndColNo = map.getSourceLine().size();
    while (EndColNo-1 &&
           (map.getSourceLine()[EndColNo-1] == ' ' ||
            map.getSourceLine()[EndColNo-1] == '\t'))
      EndColNo = map.startOfPreviousColumn(EndColNo);

    // If the start/end passed each other, then we are trying to highlight a
    // range that just exists in whitespace, which must be some sort of other
    // bug.
    assert(StartColNo <= EndColNo && "Trying to highlight whitespace??");
  }

  assert(StartColNo <= map.getSourceLine().size() && "Invalid range!");
  assert(EndColNo <= map.getSourceLine().size() && "Invalid range!");

  // Fill the range with ~'s.
  StartColNo = map.byteToContainingColumn(StartColNo);
  EndColNo = map.byteToContainingColumn(EndColNo);

  assert(StartColNo <= EndColNo && "Invalid range!");
  if (CaretLine.size() < EndColNo)
    CaretLine.resize(EndColNo,' ');
  std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~');
}
예제 #4
0
/// \brief When the source code line we want to print is too long for
/// the terminal, select the "interesting" region.
static void selectInterestingSourceRegion(std::string &SourceLine,
                                          std::string &CaretLine,
                                          std::string &FixItInsertionLine,
                                          unsigned Columns,
                                          const SourceColumnMap &map) {
  unsigned MaxColumns = std::max<unsigned>(map.columns(),
                                           std::max(CaretLine.size(),
                                                    FixItInsertionLine.size()));
  // if the number of columns is less than the desired number we're done
  if (MaxColumns <= Columns)
    return;

  // no special characters allowed in CaretLine or FixItInsertionLine
  assert(CaretLine.end() ==
         std::find_if(CaretLine.begin(), CaretLine.end(),
         char_out_of_range(' ','~')));
  assert(FixItInsertionLine.end() ==
         std::find_if(FixItInsertionLine.begin(), FixItInsertionLine.end(),
         char_out_of_range(' ','~')));

  // Find the slice that we need to display the full caret line
  // correctly.
  unsigned CaretStart = 0, CaretEnd = CaretLine.size();
  for (; CaretStart != CaretEnd; ++CaretStart)
    if (!isspace(static_cast<unsigned char>(CaretLine[CaretStart])))
      break;

  for (; CaretEnd != CaretStart; --CaretEnd)
    if (!isspace(static_cast<unsigned char>(CaretLine[CaretEnd - 1])))
      break;

  // caret has already been inserted into CaretLine so the above whitespace
  // check is guaranteed to include the caret

  // If we have a fix-it line, make sure the slice includes all of the
  // fix-it information.
  if (!FixItInsertionLine.empty()) {
    unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size();
    for (; FixItStart != FixItEnd; ++FixItStart)
      if (!isspace(static_cast<unsigned char>(FixItInsertionLine[FixItStart])))
        break;

    for (; FixItEnd != FixItStart; --FixItEnd)
      if (!isspace(static_cast<unsigned char>(FixItInsertionLine[FixItEnd - 1])))
        break;

    CaretStart = std::min(FixItStart, CaretStart);
    CaretEnd = std::max(FixItEnd, CaretEnd);
  }

  // CaretEnd may have been set at the middle of a character
  // If it's not at a character's first column then advance it past the current
  //   character.
  while (static_cast<int>(CaretEnd) < map.columns() &&
         -1 == map.columnToByte(CaretEnd))
    ++CaretEnd;

  assert((static_cast<int>(CaretStart) > map.columns() ||
          -1!=map.columnToByte(CaretStart)) &&
         "CaretStart must not point to a column in the middle of a source"
         " line character");
  assert((static_cast<int>(CaretEnd) > map.columns() ||
          -1!=map.columnToByte(CaretEnd)) &&
         "CaretEnd must not point to a column in the middle of a source line"
         " character");

  // CaretLine[CaretStart, CaretEnd) contains all of the interesting
  // parts of the caret line. While this slice is smaller than the
  // number of columns we have, try to grow the slice to encompass
  // more context.

  unsigned SourceStart = map.columnToByte(std::min<unsigned>(CaretStart,
                                                             map.columns()));
  unsigned SourceEnd = map.columnToByte(std::min<unsigned>(CaretEnd,
                                                           map.columns()));

  unsigned CaretColumnsOutsideSource = CaretEnd-CaretStart
    - (map.byteToColumn(SourceEnd)-map.byteToColumn(SourceStart));

  char const *front_ellipse = "  ...";
  char const *front_space   = "     ";
  char const *back_ellipse = "...";
  unsigned ellipses_space = strlen(front_ellipse) + strlen(back_ellipse);

  unsigned TargetColumns = Columns;
  // Give us extra room for the ellipses
  //  and any of the caret line that extends past the source
  if (TargetColumns > ellipses_space+CaretColumnsOutsideSource)
    TargetColumns -= ellipses_space+CaretColumnsOutsideSource;

  while (SourceStart>0 || SourceEnd<SourceLine.size()) {
    bool ExpandedRegion = false;

    if (SourceStart>0) {
      unsigned NewStart = map.startOfPreviousColumn(SourceStart);

      // Skip over any whitespace we see here; we're looking for
      // another bit of interesting text.
      // FIXME: Detect non-ASCII whitespace characters too.
      while (NewStart &&
             isspace(static_cast<unsigned char>(SourceLine[NewStart])))
        NewStart = map.startOfPreviousColumn(NewStart);

      // Skip over this bit of "interesting" text.
      while (NewStart) {
        unsigned Prev = map.startOfPreviousColumn(NewStart);
        if (isspace(static_cast<unsigned char>(SourceLine[Prev])))
          break;
        NewStart = Prev;
      }

      assert(map.byteToColumn(NewStart) != -1);
      unsigned NewColumns = map.byteToColumn(SourceEnd) -
                              map.byteToColumn(NewStart);
      if (NewColumns <= TargetColumns) {
        SourceStart = NewStart;
        ExpandedRegion = true;
      }
    }

    if (SourceEnd<SourceLine.size()) {
      unsigned NewEnd = map.startOfNextColumn(SourceEnd);

      // Skip over any whitespace we see here; we're looking for
      // another bit of interesting text.
      // FIXME: Detect non-ASCII whitespace characters too.
      while (NewEnd < SourceLine.size() &&
             isspace(static_cast<unsigned char>(SourceLine[NewEnd])))
        NewEnd = map.startOfNextColumn(NewEnd);

      // Skip over this bit of "interesting" text.
      while (NewEnd < SourceLine.size() &&
             !isspace(static_cast<unsigned char>(SourceLine[NewEnd])))
        NewEnd = map.startOfNextColumn(NewEnd);

      assert(map.byteToColumn(NewEnd) != -1);
      unsigned NewColumns = map.byteToColumn(NewEnd) -
                              map.byteToColumn(SourceStart);
      if (NewColumns <= TargetColumns) {
        SourceEnd = NewEnd;
        ExpandedRegion = true;
      }
    }

    if (!ExpandedRegion)
      break;
  }

  CaretStart = map.byteToColumn(SourceStart);
  CaretEnd = map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource;

  // [CaretStart, CaretEnd) is the slice we want. Update the various
  // output lines to show only this slice, with two-space padding
  // before the lines so that it looks nicer.

  assert(CaretStart!=(unsigned)-1 && CaretEnd!=(unsigned)-1 &&
         SourceStart!=(unsigned)-1 && SourceEnd!=(unsigned)-1);
  assert(SourceStart <= SourceEnd);
  assert(CaretStart <= CaretEnd);

  unsigned BackColumnsRemoved
    = map.byteToColumn(SourceLine.size())-map.byteToColumn(SourceEnd);
  unsigned FrontColumnsRemoved = CaretStart;
  unsigned ColumnsKept = CaretEnd-CaretStart;

  // We checked up front that the line needed truncation
  assert(FrontColumnsRemoved+ColumnsKept+BackColumnsRemoved > Columns);

  // The line needs some trunctiona, and we'd prefer to keep the front
  //  if possible, so remove the back
  if (BackColumnsRemoved > strlen(back_ellipse))
    SourceLine.replace(SourceEnd, std::string::npos, back_ellipse);

  // If that's enough then we're done
  if (FrontColumnsRemoved+ColumnsKept <= Columns)
    return;

  // Otherwise remove the front as well
  if (FrontColumnsRemoved > strlen(front_ellipse)) {
    SourceLine.replace(0, SourceStart, front_ellipse);
    CaretLine.replace(0, CaretStart, front_space);
    if (!FixItInsertionLine.empty())
      FixItInsertionLine.replace(0, CaretStart, front_space);
  }
}
예제 #5
0
/// \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);
}
예제 #6
0
/// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo.
void TextDiagnostic::highlightRange(const CharSourceRange &R,
                                    unsigned LineNo, FileID FID,
                                    const SourceColumnMap &map,
                                    std::string &CaretLine) {
  if (!R.isValid()) return;

  SourceLocation Begin = SM.getExpansionLoc(R.getBegin());
  SourceLocation End = SM.getExpansionLoc(R.getEnd());

  // 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 (Begin == End && R.getEnd().isMacroID())
    End = SM.getExpansionRange(R.getEnd()).second;

  unsigned StartLineNo = SM.getExpansionLineNumber(Begin);
  if (StartLineNo > LineNo || SM.getFileID(Begin) != FID)
    return;  // No intersection.

  unsigned EndLineNo = SM.getExpansionLineNumber(End);
  if (EndLineNo < LineNo || SM.getFileID(End) != FID)
    return;  // No intersection.

  // Compute the column number of the start.
  unsigned StartColNo = 0;
  if (StartLineNo == LineNo) {
    StartColNo = SM.getExpansionColumnNumber(Begin);
    if (StartColNo) --StartColNo;  // Zero base the col #.
  }

  // Compute the column number of the end.
  unsigned EndColNo = map.getSourceLine().size();
  if (EndLineNo == LineNo) {
    EndColNo = SM.getExpansionColumnNumber(End);
    if (EndColNo) {
      --EndColNo;  // Zero base the col #.

      // Add in the length of the token, so that we cover multi-char tokens if
      // this is a token range.
      if (R.isTokenRange())
        EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts);
    } else {
      EndColNo = CaretLine.size();
    }
  }

  assert(StartColNo <= EndColNo && "Invalid range!");

  // Check that a token range does not highlight only whitespace.
  if (R.isTokenRange()) {
    // Pick the first non-whitespace column.
    while (StartColNo < map.getSourceLine().size() &&
           (map.getSourceLine()[StartColNo] == ' ' ||
            map.getSourceLine()[StartColNo] == '\t'))
      ++StartColNo;

    // Pick the last non-whitespace column.
    if (EndColNo > map.getSourceLine().size())
      EndColNo = map.getSourceLine().size();
    while (EndColNo-1 &&
           (map.getSourceLine()[EndColNo-1] == ' ' ||
            map.getSourceLine()[EndColNo-1] == '\t'))
      --EndColNo;

    // If the start/end passed each other, then we are trying to highlight a
    // range that just exists in whitespace, which must be some sort of other
    // bug.
    assert(StartColNo <= EndColNo && "Trying to highlight whitespace??");
  }

  assert(StartColNo <= map.getSourceLine().size() && "Invalid range!");
  assert(EndColNo <= map.getSourceLine().size() && "Invalid range!");

  // Fill the range with ~'s.
  StartColNo = map.byteToColumn(StartColNo);
  EndColNo = map.byteToColumn(EndColNo);

  assert(StartColNo <= EndColNo && "Invalid range!");
  if (CaretLine.size() < EndColNo)
    CaretLine.resize(EndColNo,' ');
  std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~');
}
예제 #7
0
/// 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(
    FullSourceLoc Loc, DiagnosticsEngine::Level Level,
    SmallVectorImpl<CharSourceRange> &Ranges, ArrayRef<FixItHint> Hints) {
  assert(Loc.isValid() && "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 = Loc.getDecomposedLoc();
  FileID FID = LocInfo.first;
  const SourceManager &SM = Loc.getManager();

  // Get information about the buffer it points into.
  bool Invalid = false;
  StringRef BufData = Loc.getBufferData(&Invalid);
  if (Invalid)
    return;

  unsigned CaretLineNo = Loc.getLineNumber();
  unsigned CaretColNo = Loc.getColumnNumber();

  // Arbitrarily stop showing snippets when the line is too long.
  static const size_t MaxLineLengthToPrint = 4096;
  if (CaretColNo > MaxLineLengthToPrint)
    return;

  // Find the set of lines to include.
  const unsigned MaxLines = DiagOpts->SnippetLineLimit;
  std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo};
  for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(),
                                                  E = Ranges.end();
       I != E; ++I)
    if (auto OptionalRange = findLinesForRange(*I, FID, SM))
      Lines = maybeAddRange(Lines, *OptionalRange, MaxLines);

  for (unsigned LineNo = Lines.first; LineNo != Lines.second + 1; ++LineNo) {
    const char *BufStart = BufData.data();
    const char *BufEnd = BufStart + BufData.size();

    // Rewind from the current position to the start of the line.
    const char *LineStart =
        BufStart +
        SM.getDecomposedLoc(SM.translateLineCol(FID, LineNo, 1)).second;
    if (LineStart == BufEnd)
      break;

    // Compute the line end.
    const char *LineEnd = LineStart;
    while (*LineEnd != '\n' && *LineEnd != '\r' && LineEnd != BufEnd)
      ++LineEnd;

    // Arbitrarily stop showing snippets when the line is too long.
    // FIXME: Don't print any lines in this case.
    if (size_t(LineEnd - LineStart) > MaxLineLengthToPrint)
      return;

    // Trim trailing null-bytes.
    StringRef Line(LineStart, LineEnd - LineStart);
    while (!Line.empty() && Line.back() == '\0' &&
           (LineNo != CaretLineNo || Line.size() > CaretColNo))
      Line = Line.drop_back();

    // Copy the line of code into an std::string for ease of manipulation.
    std::string SourceLine(Line.begin(), Line.end());

    // Build the byte to column map.
    const SourceColumnMap sourceColMap(SourceLine, DiagOpts->TabStop);

    // Create a line for the caret that is filled with spaces that is the same
    // number of columns as the line of source code.
    std::string CaretLine(sourceColMap.columns(), ' ');

    // 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.
    if (CaretLineNo == LineNo) {
      CaretColNo = sourceColMap.byteToContainingColumn(CaretColNo - 1);
      if (CaretLine.size() < CaretColNo + 1)
        CaretLine.resize(CaretColNo + 1, ' ');
      CaretLine[CaretColNo] = '^';
    }

    std::string FixItInsertionLine = buildFixItInsertionLine(
        FID, LineNo, sourceColMap, Hints, SM, DiagOpts.get());

    // 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.empty() && CaretLine[CaretLine.size() - 1] == ' ')
      CaretLine.erase(CaretLine.end() - 1);

    // Emit what we have computed.
    emitSnippet(SourceLine);

    if (!CaretLine.empty()) {
      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);
}