Use the new source ranges tracking feature to highlight the important pieces

of a subexpression when emitting a diagnostic.  Consider this example:

struct A { int X; };

void test1(void *P, int C) {
  return ((C*40) + *P) / 42+P;
}

void test2(struct A friendlystruct, int C) {
  return (C     *40) + friendlystruct;
}

void test3(struct A friendlystruct, int C) {
  return friendlystruct + test2(friendlystruct
                               , C);
}


clang now produces this output:

t.c:4:18: error: invalid operands to binary expression ('int' and 'void')
  return ((C*40) + *P) / 42+P;
          ~~~~~~ ^ ~~

This shows the important pieces of a nested (and potentially very complex)
expression.


t.c:8:18: error: invalid operands to binary expression ('int' and 'struct A')
  return (C     *40) + friendlystruct;
         ~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~

This shows that tabs in source files (after the 'C') and multichar tokens
(friendlystruct) are handled correctly.



t.c:12:25: error: invalid operands to binary expression ('struct A' and 'void')
  return friendlystruct + test2(friendlystruct
         ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~

This shows how multiline ranges are printed.  Any part of the range that is
not on the same line as the carat is just ignored.  This also shows that
trailing spaces on the line aren't highlighted.

llvm-svn: 39459
This commit is contained in:
Chris Lattner 2007-05-19 08:06:14 +00:00
parent f8d3197831
commit ebd1b33477
2 changed files with 90 additions and 29 deletions

View File

@ -337,14 +337,19 @@ class DiagnosticPrinterSTDERR : public DiagnosticClient {
SourceManager &SourceMgr;
SourceLocation LastWarningLoc;
HeaderSearch *TheHeaderSearch;
Preprocessor *ThePreprocessor;
public:
DiagnosticPrinterSTDERR(SourceManager &sourceMgr)
: SourceMgr(sourceMgr) {}
void setHeaderSearch(HeaderSearch &HS) { TheHeaderSearch = &HS; }
void setPreprocessor(Preprocessor &P) { ThePreprocessor = &P; }
void PrintIncludeStack(SourceLocation Pos);
void HighlightRange(const SourceRange &R, unsigned LineNo,
std::string &CaratLine, const std::string &SourceLine);
unsigned GetTokenLength(SourceLocation Loc);
virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, SourceLocation Pos,
diag::kind ID, const std::string *Strs,
unsigned NumStrs, SourceRange *Ranges,
@ -367,6 +372,81 @@ PrintIncludeStack(SourceLocation Pos) {
}
/// HighlightRange - Given a SourceRange and a line number, highlight (with ~'s)
/// any characters in LineNo that intersect the SourceRange.
void DiagnosticPrinterSTDERR::HighlightRange(const SourceRange &R,
unsigned LineNo,
std::string &CaratLine,
const std::string &SourceLine) {
assert(CaratLine.size() == SourceLine.size() &&
"Expect a correspondence between source and carat line!");
if (!R.isValid()) return;
unsigned StartLineNo = SourceMgr.getLineNumber(R.Begin());
if (StartLineNo > LineNo) return; // No intersection.
unsigned EndLineNo = SourceMgr.getLineNumber(R.End());
if (EndLineNo < LineNo) return; // No intersection.
// Compute the column number of the start.
unsigned StartColNo = 0;
if (StartLineNo == LineNo) {
StartColNo = SourceMgr.getColumnNumber(R.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 = CaratLine.size();
if (EndLineNo == LineNo) {
EndColNo = SourceMgr.getColumnNumber(R.End());
if (EndColNo) {
--EndColNo; // Zero base the col #.
// Add in the length of the token, so that we cover multi-char tokens.
EndColNo += GetTokenLength(R.End());
} else {
EndColNo = CaratLine.size();
}
}
// Pick the last non-whitespace column.
while (EndColNo-1 &&
(SourceLine[EndColNo-1] == ' ' || SourceLine[EndColNo-1] == '\t'))
--EndColNo;
// Fill the range with ~'s.
assert(StartColNo <= EndColNo && "Invalid range!");
for (unsigned i = StartColNo; i != EndColNo; ++i)
CaratLine[i] = '~';
}
/// GetTokenLength - Given the source location of a token, determine its length.
/// This is a fully general function that uses a lexer to relex the token.
unsigned DiagnosticPrinterSTDERR::GetTokenLength(SourceLocation Loc) {
const char *StrData = SourceMgr.getCharacterData(Loc);
// Note, this could be special cased for common tokens like identifiers, ')',
// etc to make this faster, if it mattered.
unsigned FileID = Loc.getFileID();
// Create a lexer starting at the beginning of this token.
Lexer TheLexer(SourceMgr.getBuffer(FileID), FileID,
*ThePreprocessor, StrData);
LexerToken TheTok;
TheLexer.LexRawToken(TheTok);
return TheTok.getLength();
}
void DiagnosticPrinterSTDERR::HandleDiagnostic(Diagnostic::Level Level,
SourceLocation Pos,
diag::kind ID,
@ -456,8 +536,9 @@ void DiagnosticPrinterSTDERR::HandleDiagnostic(Diagnostic::Level Level,
// length as the line of source code.
std::string CaratLine(LineEnd-LineStart, ' ');
// FIXME: if (NumRanges) use Ranges to output fancy highlighting
// Print out the caret itself.
// Highlight all of the characters covered by Ranges with ~ characters.
for (unsigned i = 0; i != NumRanges; ++i)
HighlightRange(Ranges[i], LineNo, CaratLine, SourceLine);
// Next, insert the carat itself.
if (ColNo < CaratLine.size())
@ -481,7 +562,7 @@ void DiagnosticPrinterSTDERR::HandleDiagnostic(Diagnostic::Level Level,
SourceLine.insert(i+1, NumSpaces, ' ');
// Insert spaces or ~'s into CaratLine.
CaratLine.insert(i+1, NumSpaces, CaratLine[0] == '~' ? '~' : ' ');
CaratLine.insert(i+1, NumSpaces, CaratLine[i] == '~' ? '~' : ' ');
}
// Finally, remove any blank spaces from the end of CaratLine.
@ -950,6 +1031,7 @@ static void PrintASTs(Preprocessor &PP, unsigned MainFileID) {
///
static void ProcessInputFile(const std::string &InFile,
SourceManager &SourceMgr, Diagnostic &Diags,
DiagnosticPrinterSTDERR &OurDiagnosticClient,
HeaderSearch &HeaderInfo, TargetInfo &Target,
const LangOptions &LangInfo) {
FileManager &FileMgr = HeaderInfo.getFileMgr();
@ -957,6 +1039,8 @@ static void ProcessInputFile(const std::string &InFile,
// Set up the preprocessor with these options.
Preprocessor PP(Diags, LangInfo, Target, SourceMgr, HeaderInfo);
OurDiagnosticClient.setPreprocessor(PP);
// Install things like __POWERPC__, __GNUC__, etc into the macro table.
std::vector<char> PrologMacros;
InitializePredefinedMacros(PP, PrologMacros);
@ -1108,7 +1192,7 @@ int main(int argc, char **argv) {
InitializeIncludePaths(HeaderInfo, FileMgr, Diags, LangInfo);
for (unsigned i = 0, e = InputFilenames.size(); i != e; ++i)
ProcessInputFile(InputFilenames[i], SourceMgr, Diags,
ProcessInputFile(InputFilenames[i], SourceMgr, Diags, OurDiagnosticClient,
HeaderInfo, *Target, LangInfo);
if (NumDiagnostics)

View File

@ -94,23 +94,6 @@
DED7D9E50A5257F6003AD0FB /* ScratchBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DED7D9E40A5257F6003AD0FB /* ScratchBuffer.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXBuildStyle section */
84D221650BFB885000DAAD04 /* Development */ = {
isa = PBXBuildStyle;
buildSettings = {
COPY_PHASE_STRIP = NO;
};
name = Development;
};
84D221660BFB885000DAAD04 /* Deployment */ = {
isa = PBXBuildStyle;
buildSettings = {
COPY_PHASE_STRIP = YES;
};
name = Deployment;
};
/* End PBXBuildStyle section */
/* Begin PBXCopyFilesBuildPhase section */
8DD76F690486A84900D96B5E /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
@ -171,7 +154,7 @@
1A30A9E80B93A4C800201A91 /* ExprCXX.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ExprCXX.h; path = clang/AST/ExprCXX.h; sourceTree = "<group>"; };
1A869A6E0BA2164C008DA07A /* LiteralSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LiteralSupport.h; sourceTree = "<group>"; };
1A869AA70BA21ABA008DA07A /* LiteralSupport.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = LiteralSupport.cpp; sourceTree = "<group>"; };
8DD76F6C0486A84900D96B5E /* clang */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "compiled.mach-o.executable"; path = clang; sourceTree = BUILT_PRODUCTS_DIR; };
8DD76F6C0486A84900D96B5E /* clang */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = clang; sourceTree = BUILT_PRODUCTS_DIR; };
DE01DA480B12ADA300AC22CE /* PPCallbacks.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PPCallbacks.h; sourceTree = "<group>"; };
DE06B73D0A8307640050E87E /* LangOptions.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = LangOptions.h; sourceTree = "<group>"; };
DE06BECA0A854E4B0050E87E /* Scope.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Scope.h; path = clang/Parse/Scope.h; sourceTree = "<group>"; };
@ -493,12 +476,6 @@
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "clang" */;
buildSettings = {
};
buildStyles = (
84D221650BFB885000DAAD04 /* Development */,
84D221660BFB885000DAAD04 /* Deployment */,
);
hasScannedForEncodings = 1;
mainGroup = 08FB7794FE84155DC02AAC07 /* clang */;
projectDirPath = "";