forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			363 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			363 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===//
 | 
						|
//
 | 
						|
//                     The LLVM Compiler Infrastructure
 | 
						|
//
 | 
						|
// This file is distributed under the University of Illinois Open Source
 | 
						|
// License. See LICENSE.TXT for details.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// This diagnostic client prints out their diagnostic messages.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "clang/Frontend/TextDiagnosticPrinter.h"
 | 
						|
#include "clang/Basic/SourceManager.h"
 | 
						|
#include "clang/Lex/Lexer.h"
 | 
						|
#include "llvm/Support/raw_ostream.h"
 | 
						|
#include "llvm/ADT/SmallString.h"
 | 
						|
#include <algorithm>
 | 
						|
using namespace clang;
 | 
						|
 | 
						|
void TextDiagnosticPrinter::
 | 
						|
PrintIncludeStack(SourceLocation Loc, const SourceManager &SM) {
 | 
						|
  if (Loc.isInvalid()) return;
 | 
						|
 | 
						|
  PresumedLoc PLoc = SM.getPresumedLoc(Loc);
 | 
						|
 | 
						|
  // Print out the other include frames first.
 | 
						|
  PrintIncludeStack(PLoc.getIncludeLoc(), SM);
 | 
						|
 | 
						|
  if (ShowLocation)
 | 
						|
    OS << "In file included from " << PLoc.getFilename()
 | 
						|
       << ':' << PLoc.getLine() << ":\n";
 | 
						|
  else
 | 
						|
    OS << "In included file:\n";
 | 
						|
}
 | 
						|
 | 
						|
/// HighlightRange - Given a SourceRange and a line number, highlight (with ~'s)
 | 
						|
/// any characters in LineNo that intersect the SourceRange.
 | 
						|
void TextDiagnosticPrinter::HighlightRange(const SourceRange &R,
 | 
						|
                                           const SourceManager &SM,
 | 
						|
                                           unsigned LineNo, FileID FID,
 | 
						|
                                           std::string &CaretLine,
 | 
						|
                                           const std::string &SourceLine) {
 | 
						|
  assert(CaretLine.size() == SourceLine.size() &&
 | 
						|
         "Expect a correspondence between source and caret line!");
 | 
						|
  if (!R.isValid()) return;
 | 
						|
 | 
						|
  SourceLocation Begin = SM.getInstantiationLoc(R.getBegin());
 | 
						|
  SourceLocation End = SM.getInstantiationLoc(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.getInstantiationRange(R.getEnd()).second;
 | 
						|
  
 | 
						|
  unsigned StartLineNo = SM.getInstantiationLineNumber(Begin);
 | 
						|
  if (StartLineNo > LineNo || SM.getFileID(Begin) != FID)
 | 
						|
    return;  // No intersection.
 | 
						|
  
 | 
						|
  unsigned EndLineNo = SM.getInstantiationLineNumber(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.getInstantiationColumnNumber(Begin);
 | 
						|
    if (StartColNo) --StartColNo;  // Zero base the col #.
 | 
						|
  }
 | 
						|
 | 
						|
  // Pick the first non-whitespace column.
 | 
						|
  while (StartColNo < SourceLine.size() &&
 | 
						|
         (SourceLine[StartColNo] == ' ' || SourceLine[StartColNo] == '\t'))
 | 
						|
    ++StartColNo;
 | 
						|
  
 | 
						|
  // Compute the column number of the end.
 | 
						|
  unsigned EndColNo = CaretLine.size();
 | 
						|
  if (EndLineNo == LineNo) {
 | 
						|
    EndColNo = SM.getInstantiationColumnNumber(End);
 | 
						|
    if (EndColNo) {
 | 
						|
      --EndColNo;  // Zero base the col #.
 | 
						|
      
 | 
						|
      // Add in the length of the token, so that we cover multi-char tokens.
 | 
						|
      EndColNo += Lexer::MeasureTokenLength(End, SM, *LangOpts);
 | 
						|
    } else {
 | 
						|
      EndColNo = CaretLine.size();
 | 
						|
    }
 | 
						|
  }
 | 
						|
  
 | 
						|
  // Pick the last non-whitespace column.
 | 
						|
  if (EndColNo <= SourceLine.size())
 | 
						|
    while (EndColNo-1 &&
 | 
						|
           (SourceLine[EndColNo-1] == ' ' || SourceLine[EndColNo-1] == '\t'))
 | 
						|
      --EndColNo;
 | 
						|
  else
 | 
						|
    EndColNo = SourceLine.size();
 | 
						|
  
 | 
						|
  // Fill the range with ~'s.
 | 
						|
  assert(StartColNo <= EndColNo && "Invalid range!");
 | 
						|
  for (unsigned i = StartColNo; i < EndColNo; ++i)
 | 
						|
    CaretLine[i] = '~';
 | 
						|
}
 | 
						|
 | 
						|
void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc,
 | 
						|
                                                SourceRange *Ranges,
 | 
						|
                                                unsigned NumRanges,
 | 
						|
                                                SourceManager &SM,
 | 
						|
                                          const CodeModificationHint *Hints,
 | 
						|
                                                unsigned NumHints) {
 | 
						|
  assert(!Loc.isInvalid() && "must have a valid source location here");
 | 
						|
  
 | 
						|
  // We always emit diagnostics about the instantiation points, not the spelling
 | 
						|
  // points.  This more closely correlates to what the user writes.
 | 
						|
  if (!Loc.isFileID()) {
 | 
						|
    SourceLocation OneLevelUp = SM.getImmediateInstantiationRange(Loc).first;
 | 
						|
    EmitCaretDiagnostic(OneLevelUp, Ranges, NumRanges, SM);
 | 
						|
    
 | 
						|
    // Map the location through the macro.
 | 
						|
    Loc = SM.getInstantiationLoc(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.getInstantiationLoc(SM.getImmediateSpellingLoc(S));
 | 
						|
      if (E.isMacroID())
 | 
						|
        E = SM.getInstantiationLoc(SM.getImmediateSpellingLoc(E));
 | 
						|
      Ranges[i] = SourceRange(S, E);
 | 
						|
    }
 | 
						|
    
 | 
						|
    if (ShowLocation) {
 | 
						|
      // Emit the file/line/column that this expansion came from.
 | 
						|
      OS << SM.getBufferName(Loc) << ':' << SM.getInstantiationLineNumber(Loc)
 | 
						|
         << ':';
 | 
						|
      if (ShowColumn)
 | 
						|
        OS << SM.getInstantiationColumnNumber(Loc) << ':';
 | 
						|
      OS << ' ';
 | 
						|
    }
 | 
						|
    OS << "note: instantiated from:\n";
 | 
						|
  }
 | 
						|
  
 | 
						|
  // 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);
 | 
						|
  
 | 
						|
  // 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] == '~' ? '~' : ' ');
 | 
						|
  }
 | 
						|
  
 | 
						|
  // 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';
 | 
						|
  OS << CaretLine << '\n';
 | 
						|
 | 
						|
  if (NumHints && PrintFixItInfo) {
 | 
						|
    std::string InsertionLine;
 | 
						|
    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 > InsertionLine.size())
 | 
						|
            InsertionLine.resize(LastColumnModified, ' ');
 | 
						|
          std::copy(Hint->CodeToInsert.begin(), Hint->CodeToInsert.end(),
 | 
						|
                    InsertionLine.begin() + HintColNo - 1);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!InsertionLine.empty())
 | 
						|
      OS << InsertionLine << '\n';
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, 
 | 
						|
                                             const DiagnosticInfo &Info) {
 | 
						|
  // If the location is specified, print out a file/line/col and include trace
 | 
						|
  // if enabled.
 | 
						|
  if (Info.getLocation().isValid()) {
 | 
						|
    const SourceManager &SM = Info.getLocation().getManager();
 | 
						|
    PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation());
 | 
						|
    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(LastWarningLoc, SM);
 | 
						|
    }
 | 
						|
  
 | 
						|
    // Compute the column number.
 | 
						|
    if (ShowLocation) {
 | 
						|
      OS << PLoc.getFilename() << ':' << LineNo << ':';
 | 
						|
      if (ShowColumn)
 | 
						|
        if (unsigned ColNo = PLoc.getColumn())
 | 
						|
          OS << ColNo << ':';
 | 
						|
      
 | 
						|
      if (PrintRangeInfo && 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();
 | 
						|
          std::pair<FileID, unsigned> BInfo=SM.getDecomposedInstantiationLoc(B);
 | 
						|
          
 | 
						|
          E = SM.getInstantiationLoc(E);
 | 
						|
          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 = 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 << ' ';
 | 
						|
    }
 | 
						|
  }
 | 
						|
  
 | 
						|
  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;
 | 
						|
  }
 | 
						|
  
 | 
						|
  llvm::SmallString<100> OutStr;
 | 
						|
  Info.FormatDiagnostic(OutStr);
 | 
						|
  OS.write(OutStr.begin(), OutStr.size());
 | 
						|
  
 | 
						|
  if (PrintDiagnosticOption)
 | 
						|
    if (const char *Option = Diagnostic::getWarningOptionForDiag(Info.getID()))
 | 
						|
      OS << " [-W" << Option << ']';
 | 
						|
  
 | 
						|
  OS << '\n';
 | 
						|
  
 | 
						|
  // 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 (CaretDiagnostics && Info.getLocation().isValid() &&
 | 
						|
      ((LastLoc != Info.getLocation()) || Info.getNumRanges() || 
 | 
						|
       (LastCaretDiagnosticWasNote && Level != Diagnostic::Note) ||
 | 
						|
       Info.getNumCodeModificationHints())) {
 | 
						|
    // Cache the LastLoc, it allows us to omit duplicate source/caret spewage.
 | 
						|
    LastLoc = Info.getLocation();
 | 
						|
    LastCaretDiagnosticWasNote = (Level == Diagnostic::Note);
 | 
						|
 | 
						|
    // Get the ranges into a local array we can hack on.
 | 
						|
    SourceRange 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.getNumCodeModificationHints();
 | 
						|
    for (unsigned idx = 0; idx < NumHints; ++idx) {
 | 
						|
      const CodeModificationHint &Hint = Info.getCodeModificationHint(idx);
 | 
						|
      if (Hint.RemoveRange.isValid()) {
 | 
						|
        assert(NumRanges < 20 && "Out of space");
 | 
						|
        Ranges[NumRanges++] = Hint.RemoveRange;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(),
 | 
						|
                        Info.getCodeModificationHints(),
 | 
						|
                        Info.getNumCodeModificationHints());
 | 
						|
  }
 | 
						|
  
 | 
						|
  OS.flush();
 | 
						|
}
 |