forked from OSchip/llvm-project
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:
parent
f8d3197831
commit
ebd1b33477
|
@ -337,14 +337,19 @@ class DiagnosticPrinterSTDERR : public DiagnosticClient {
|
||||||
SourceManager &SourceMgr;
|
SourceManager &SourceMgr;
|
||||||
SourceLocation LastWarningLoc;
|
SourceLocation LastWarningLoc;
|
||||||
HeaderSearch *TheHeaderSearch;
|
HeaderSearch *TheHeaderSearch;
|
||||||
|
Preprocessor *ThePreprocessor;
|
||||||
public:
|
public:
|
||||||
DiagnosticPrinterSTDERR(SourceManager &sourceMgr)
|
DiagnosticPrinterSTDERR(SourceManager &sourceMgr)
|
||||||
: SourceMgr(sourceMgr) {}
|
: SourceMgr(sourceMgr) {}
|
||||||
|
|
||||||
void setHeaderSearch(HeaderSearch &HS) { TheHeaderSearch = &HS; }
|
void setHeaderSearch(HeaderSearch &HS) { TheHeaderSearch = &HS; }
|
||||||
|
void setPreprocessor(Preprocessor &P) { ThePreprocessor = &P; }
|
||||||
|
|
||||||
void PrintIncludeStack(SourceLocation Pos);
|
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,
|
virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, SourceLocation Pos,
|
||||||
diag::kind ID, const std::string *Strs,
|
diag::kind ID, const std::string *Strs,
|
||||||
unsigned NumStrs, SourceRange *Ranges,
|
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,
|
void DiagnosticPrinterSTDERR::HandleDiagnostic(Diagnostic::Level Level,
|
||||||
SourceLocation Pos,
|
SourceLocation Pos,
|
||||||
diag::kind ID,
|
diag::kind ID,
|
||||||
|
@ -456,8 +536,9 @@ void DiagnosticPrinterSTDERR::HandleDiagnostic(Diagnostic::Level Level,
|
||||||
// length as the line of source code.
|
// length as the line of source code.
|
||||||
std::string CaratLine(LineEnd-LineStart, ' ');
|
std::string CaratLine(LineEnd-LineStart, ' ');
|
||||||
|
|
||||||
// FIXME: if (NumRanges) use Ranges to output fancy highlighting
|
// Highlight all of the characters covered by Ranges with ~ characters.
|
||||||
// Print out the caret itself.
|
for (unsigned i = 0; i != NumRanges; ++i)
|
||||||
|
HighlightRange(Ranges[i], LineNo, CaratLine, SourceLine);
|
||||||
|
|
||||||
// Next, insert the carat itself.
|
// Next, insert the carat itself.
|
||||||
if (ColNo < CaratLine.size())
|
if (ColNo < CaratLine.size())
|
||||||
|
@ -481,7 +562,7 @@ void DiagnosticPrinterSTDERR::HandleDiagnostic(Diagnostic::Level Level,
|
||||||
SourceLine.insert(i+1, NumSpaces, ' ');
|
SourceLine.insert(i+1, NumSpaces, ' ');
|
||||||
|
|
||||||
// Insert spaces or ~'s into CaratLine.
|
// 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.
|
// 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,
|
static void ProcessInputFile(const std::string &InFile,
|
||||||
SourceManager &SourceMgr, Diagnostic &Diags,
|
SourceManager &SourceMgr, Diagnostic &Diags,
|
||||||
|
DiagnosticPrinterSTDERR &OurDiagnosticClient,
|
||||||
HeaderSearch &HeaderInfo, TargetInfo &Target,
|
HeaderSearch &HeaderInfo, TargetInfo &Target,
|
||||||
const LangOptions &LangInfo) {
|
const LangOptions &LangInfo) {
|
||||||
FileManager &FileMgr = HeaderInfo.getFileMgr();
|
FileManager &FileMgr = HeaderInfo.getFileMgr();
|
||||||
|
@ -957,6 +1039,8 @@ static void ProcessInputFile(const std::string &InFile,
|
||||||
// Set up the preprocessor with these options.
|
// Set up the preprocessor with these options.
|
||||||
Preprocessor PP(Diags, LangInfo, Target, SourceMgr, HeaderInfo);
|
Preprocessor PP(Diags, LangInfo, Target, SourceMgr, HeaderInfo);
|
||||||
|
|
||||||
|
OurDiagnosticClient.setPreprocessor(PP);
|
||||||
|
|
||||||
// Install things like __POWERPC__, __GNUC__, etc into the macro table.
|
// Install things like __POWERPC__, __GNUC__, etc into the macro table.
|
||||||
std::vector<char> PrologMacros;
|
std::vector<char> PrologMacros;
|
||||||
InitializePredefinedMacros(PP, PrologMacros);
|
InitializePredefinedMacros(PP, PrologMacros);
|
||||||
|
@ -1108,7 +1192,7 @@ int main(int argc, char **argv) {
|
||||||
InitializeIncludePaths(HeaderInfo, FileMgr, Diags, LangInfo);
|
InitializeIncludePaths(HeaderInfo, FileMgr, Diags, LangInfo);
|
||||||
|
|
||||||
for (unsigned i = 0, e = InputFilenames.size(); i != e; ++i)
|
for (unsigned i = 0, e = InputFilenames.size(); i != e; ++i)
|
||||||
ProcessInputFile(InputFilenames[i], SourceMgr, Diags,
|
ProcessInputFile(InputFilenames[i], SourceMgr, Diags, OurDiagnosticClient,
|
||||||
HeaderInfo, *Target, LangInfo);
|
HeaderInfo, *Target, LangInfo);
|
||||||
|
|
||||||
if (NumDiagnostics)
|
if (NumDiagnostics)
|
||||||
|
|
|
@ -94,23 +94,6 @@
|
||||||
DED7D9E50A5257F6003AD0FB /* ScratchBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DED7D9E40A5257F6003AD0FB /* ScratchBuffer.cpp */; };
|
DED7D9E50A5257F6003AD0FB /* ScratchBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DED7D9E40A5257F6003AD0FB /* ScratchBuffer.cpp */; };
|
||||||
/* End PBXBuildFile section */
|
/* 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 */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
8DD76F690486A84900D96B5E /* CopyFiles */ = {
|
8DD76F690486A84900D96B5E /* CopyFiles */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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 */ = {
|
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "clang" */;
|
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "clang" */;
|
||||||
buildSettings = {
|
|
||||||
};
|
|
||||||
buildStyles = (
|
|
||||||
84D221650BFB885000DAAD04 /* Development */,
|
|
||||||
84D221660BFB885000DAAD04 /* Deployment */,
|
|
||||||
);
|
|
||||||
hasScannedForEncodings = 1;
|
hasScannedForEncodings = 1;
|
||||||
mainGroup = 08FB7794FE84155DC02AAC07 /* clang */;
|
mainGroup = 08FB7794FE84155DC02AAC07 /* clang */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
|
|
Loading…
Reference in New Issue