diff options
-rw-r--r-- | include/clang/Basic/SourceManager.h | 80 | ||||
-rw-r--r-- | lib/Basic/SourceManager.cpp | 31 | ||||
-rw-r--r-- | lib/Frontend/TextDiagnosticPrinter.cpp | 96 | ||||
-rw-r--r-- | lib/Lex/TokenLexer.cpp | 6 | ||||
-rw-r--r-- | test/Misc/caret-diags-macros.c | 68 |
5 files changed, 238 insertions, 43 deletions
diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h index bd8f5ef6ef..6301f31978 100644 --- a/include/clang/Basic/SourceManager.h +++ b/include/clang/Basic/SourceManager.h @@ -239,8 +239,11 @@ namespace SrcMgr { /// InstantiationLocStart/InstantiationLocEnd - In a macro expansion, these /// indicate the start and end of the instantiation. In object-like macros, /// these will be the same. In a function-like macro instantiation, the - /// start will be the identifier and the end will be the ')'. + /// start will be the identifier and the end will be the ')'. Finally, in + /// macro-argument instantitions, the end will be 'SourceLocation()', an + /// invalid location. unsigned InstantiationLocStart, InstantiationLocEnd; + public: SourceLocation getSpellingLoc() const { return SourceLocation::getFromRawEncoding(SpellingLoc); @@ -249,7 +252,9 @@ namespace SrcMgr { return SourceLocation::getFromRawEncoding(InstantiationLocStart); } SourceLocation getInstantiationLocEnd() const { - return SourceLocation::getFromRawEncoding(InstantiationLocEnd); + SourceLocation EndLoc = + SourceLocation::getFromRawEncoding(InstantiationLocEnd); + return EndLoc.isInvalid() ? getInstantiationLocStart() : EndLoc; } std::pair<SourceLocation,SourceLocation> getInstantiationLocRange() const { @@ -257,19 +262,52 @@ namespace SrcMgr { getInstantiationLocEnd()); } - /// get - Return a InstantiationInfo for an expansion. IL specifies - /// the instantiation location (where the macro is expanded), and SL - /// specifies the spelling location (where the characters from the token - /// come from). IL and PL can both refer to normal File SLocs or + bool isMacroArgInstantiation() const { + // Note that this needs to return false for default constructed objects. + return getInstantiationLocStart().isValid() && + SourceLocation::getFromRawEncoding(InstantiationLocEnd).isInvalid(); + } + + /// create - Return a InstantiationInfo for an expansion. ILStart and + /// ILEnd specify the instantiation range (where the macro is expanded), + /// and SL specifies the spelling location (where the characters from the + /// token come from). All three can refer to normal File SLocs or /// instantiation locations. - static InstantiationInfo get(SourceLocation ILStart, SourceLocation ILEnd, - SourceLocation SL) { + static InstantiationInfo create(SourceLocation SL, + SourceLocation ILStart, + SourceLocation ILEnd) { InstantiationInfo X; X.SpellingLoc = SL.getRawEncoding(); X.InstantiationLocStart = ILStart.getRawEncoding(); X.InstantiationLocEnd = ILEnd.getRawEncoding(); return X; } + + /// createForMacroArg - Return a special InstantiationInfo for the + /// expansion of a macro argument into a function-like macro's body. IL + /// specifies the instantiation location (where the macro is expanded). + /// This doesn't need to be a range because a macro is always instantiated + /// at a macro parameter reference, and macro parameters are always exactly + /// one token. SL specifies the spelling location (where the characters + /// from the token come from). IL and SL can both refer to normal File + /// SLocs or instantiation locations. + /// + /// Given the code: + /// \code + /// #define F(x) f(x) + /// F(42); + /// \endcode + /// + /// When expanding '\c F(42)', the '\c x' would call this with an SL + /// pointing at '\c 42' anad an IL pointing at its location in the + /// definition of '\c F'. + static InstantiationInfo createForMacroArg(SourceLocation SL, + SourceLocation IL) { + // We store an intentionally invalid source location for the end of the + // instantiation range to mark that this is a macro argument instantation + // rather than a normal one. + return create(SL, IL, SourceLocation()); + } }; /// SLocEntry - This is a discriminated union of FileInfo and @@ -533,9 +571,17 @@ public: return MainFileID; } + /// createMacroArgInstantiationLoc - Return a new SourceLocation that encodes + /// the fact that a token from SpellingLoc should actually be referenced from + /// InstantiationLoc, and that it represents the instantiation of a macro + /// argument into the function-like macro body. + SourceLocation createMacroArgInstantiationLoc(SourceLocation Loc, + SourceLocation InstantiationLoc, + unsigned TokLength); + /// createInstantiationLoc - Return a new SourceLocation that encodes the fact - /// that a token at Loc should actually be referenced from InstantiationLoc. - /// TokLength is the length of the token being instantiated. + /// that a token from SpellingLoc should actually be referenced from + /// InstantiationLoc. SourceLocation createInstantiationLoc(SourceLocation Loc, SourceLocation InstantiationLocStart, SourceLocation InstantiationLocEnd, @@ -746,6 +792,12 @@ public: return getDecomposedLoc(SpellingLoc).second; } + /// isMacroArgInstantiation - This method tests whether the given source + /// location represents a macro argument's instantiation into the + /// function-like macro definition. Such source locations only appear inside + /// of the instantiation locations representing where a particular + /// function-like macro was expanded. + bool isMacroArgInstantiation(SourceLocation Loc) const; //===--------------------------------------------------------------------===// // Queries about the code at a SourceLocation. @@ -991,6 +1043,14 @@ public: private: const llvm::MemoryBuffer *getFakeBufferForRecovery() const; + /// createInstantiationLoc - Implements the common elements of storing an + /// instantiation info struct into the SLocEntry table and producing a source + /// location that refers to it. + SourceLocation createInstantiationLocImpl(const SrcMgr::InstantiationInfo &II, + unsigned TokLength, + unsigned PreallocatedID = 0, + unsigned Offset = 0); + /// isOffsetInFileID - Return true if the specified FileID contains the /// specified SourceLocation offset. This is a very hot method. inline bool isOffsetInFileID(FileID FID, unsigned SLocOffset) const { diff --git a/lib/Basic/SourceManager.cpp b/lib/Basic/SourceManager.cpp index cd098b23aa..45922c1552 100644 --- a/lib/Basic/SourceManager.cpp +++ b/lib/Basic/SourceManager.cpp @@ -531,16 +531,31 @@ FileID SourceManager::createFileID(const ContentCache *File, return LastFileIDLookup = FID; } -/// createInstantiationLoc - Return a new SourceLocation that encodes the fact -/// that a token from SpellingLoc should actually be referenced from -/// InstantiationLoc. +SourceLocation +SourceManager::createMacroArgInstantiationLoc(SourceLocation SpellingLoc, + SourceLocation ILoc, + unsigned TokLength) { + InstantiationInfo II = + InstantiationInfo::createForMacroArg(SpellingLoc, ILoc); + return createInstantiationLocImpl(II, TokLength); +} + SourceLocation SourceManager::createInstantiationLoc(SourceLocation SpellingLoc, SourceLocation ILocStart, SourceLocation ILocEnd, unsigned TokLength, unsigned PreallocatedID, unsigned Offset) { - InstantiationInfo II = InstantiationInfo::get(ILocStart,ILocEnd, SpellingLoc); + InstantiationInfo II = + InstantiationInfo::create(SpellingLoc, ILocStart, ILocEnd); + return createInstantiationLocImpl(II, TokLength, PreallocatedID, Offset); +} + +SourceLocation +SourceManager::createInstantiationLocImpl(const InstantiationInfo &II, + unsigned TokLength, + unsigned PreallocatedID, + unsigned Offset) { if (PreallocatedID) { // If we're filling in a preallocated ID, just load in the // instantiation entry and return. @@ -824,6 +839,14 @@ SourceManager::getInstantiationRange(SourceLocation Loc) const { return Res; } +bool SourceManager::isMacroArgInstantiation(SourceLocation Loc) const { + if (!Loc.isMacroID()) return false; + + FileID FID = getFileID(Loc); + const SrcMgr::SLocEntry *E = &getSLocEntry(FID); + const SrcMgr::InstantiationInfo &II = E->getInstantiation(); + return II.isMacroArgInstantiation(); +} //===----------------------------------------------------------------------===// diff --git a/lib/Frontend/TextDiagnosticPrinter.cpp b/lib/Frontend/TextDiagnosticPrinter.cpp index affa12babe..570a6cda05 100644 --- a/lib/Frontend/TextDiagnosticPrinter.cpp +++ b/lib/Frontend/TextDiagnosticPrinter.cpp @@ -292,6 +292,56 @@ static void SelectInterestingSourceRegion(std::string &SourceLine, } } +/// Look through spelling locations for a macro argument instantiation, and +/// if found skip to it so that we can trace the argument rather than the macros +/// in which that argument is used. If no macro argument instantiation is found, +/// don't skip anything and return the starting location. +static SourceLocation skipToMacroArgInstantiation(const SourceManager &SM, + SourceLocation StartLoc) { + for (SourceLocation L = StartLoc; L.isMacroID(); + L = SM.getImmediateSpellingLoc(L)) { + if (SM.isMacroArgInstantiation(L)) + return L; + } + + // Otherwise just return initial location, there's nothing to skip. + return StartLoc; +} + +/// Gets the location of the immediate macro caller, one level up the stack +/// toward the initial macro typed into the source. +static SourceLocation getImmediateMacroCallerLoc(const SourceManager &SM, + SourceLocation Loc) { + if (!Loc.isMacroID()) return Loc; + + // When we have the location of (part of) an expanded parameter, its spelling + // location points to the argument as typed into the macro call, and + // therefore is used to locate the macro caller. + if (SM.isMacroArgInstantiation(Loc)) + return SM.getImmediateSpellingLoc(Loc); + + // Otherwise, the caller of the macro is located where this macro is + // instantiated (while the spelling is part of the macro definition). + return SM.getImmediateInstantiationRange(Loc).first; +} + +/// Gets the location of the immediate macro callee, one level down the stack +/// toward the leaf macro. +static SourceLocation getImmediateMacroCalleeLoc(const SourceManager &SM, + SourceLocation Loc) { + if (!Loc.isMacroID()) return Loc; + + // When we have the location of (part of) an expanded parameter, its + // instantiation location points to the unexpanded paramater reference within + // the macro definition (or callee). + if (SM.isMacroArgInstantiation(Loc)) + return SM.getImmediateInstantiationRange(Loc).first; + + // Otherwise, the callee of the macro is located where this location was + // spelled inside the macro definition. + return SM.getImmediateSpellingLoc(Loc); +} + void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc, CharSourceRange *Ranges, unsigned NumRanges, @@ -312,33 +362,40 @@ void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc, // Whether to suppress printing this macro instantiation. bool Suppressed = OnMacroInst >= MacroSkipStart && OnMacroInst < MacroSkipEnd; - - SourceLocation OneLevelUp = SM.getImmediateInstantiationRange(Loc).first; - + + // When processing macros, skip over the instantiations leading up to + // a macro argument, and trace the argument's instantiation stack instead. + Loc = skipToMacroArgInstantiation(SM, Loc); + + SourceLocation OneLevelUp = getImmediateMacroCallerLoc(SM, Loc); + // FIXME: Map ranges? EmitCaretDiagnostic(OneLevelUp, Ranges, NumRanges, SM, Hints, NumHints, Columns, OnMacroInst + 1, MacroSkipStart, MacroSkipEnd); - + // Map the location. - Loc = SM.getImmediateSpellingLoc(Loc); + Loc = getImmediateMacroCalleeLoc(SM, Loc); // Map the ranges. for (unsigned i = 0; i != NumRanges; ++i) { CharSourceRange &R = Ranges[i]; SourceLocation S = R.getBegin(), E = R.getEnd(); if (S.isMacroID()) - R.setBegin(SM.getImmediateSpellingLoc(S)); + R.setBegin(getImmediateMacroCalleeLoc(SM, S)); if (E.isMacroID()) - R.setEnd(SM.getImmediateSpellingLoc(E)); + R.setEnd(getImmediateMacroCalleeLoc(SM, E)); } if (!Suppressed) { + // Don't print recursive instantiation notes from an instantiation note. + Loc = SM.getSpellingLoc(Loc); + // Get the pretty name, according to #line directives etc. PresumedLoc PLoc = SM.getPresumedLoc(Loc); if (PLoc.isInvalid()) return; - + // If this diagnostic is not in the main file, print out the // "included from" lines. if (LastWarningLoc != PLoc.getIncludeLoc()) { @@ -354,9 +411,6 @@ void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc, OS << ' '; } OS << "note: instantiated from:\n"; - - // Don't print recursive instantiation notes from an instantiation note. - Loc = SM.getSpellingLoc(Loc); EmitCaretDiagnostic(Loc, Ranges, NumRanges, SM, 0, 0, Columns, OnMacroInst + 1, MacroSkipStart, @@ -772,6 +826,20 @@ static bool PrintWordWrapped(llvm::raw_ostream &OS, return true; } +/// Get the presumed location of a diagnostic message. This computes the +/// presumed location for the top of any macro backtrace when present. +static PresumedLoc getDiagnosticPresumedLoc(const SourceManager &SM, + SourceLocation Loc) { + // This is a condensed form of the algorithm used by EmitCaretDiagnostic to + // walk to the top of the macro call stack. + while (Loc.isMacroID()) { + Loc = skipToMacroArgInstantiation(SM, Loc); + Loc = getImmediateMacroCallerLoc(SM, Loc); + } + + return SM.getPresumedLoc(Loc); +} + void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, const DiagnosticInfo &Info) { // Default implementation (Warnings/errors count). @@ -790,7 +858,7 @@ void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, // if enabled. if (Info.getLocation().isValid()) { const SourceManager &SM = Info.getSourceManager(); - PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation()); + PresumedLoc PLoc = getDiagnosticPresumedLoc(SM, Info.getLocation()); if (PLoc.isInvalid()) { // At least print the file name if available: FileID FID = SM.getFileID(Info.getLocation()); @@ -1043,6 +1111,7 @@ void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, } } + const SourceManager &SM = LastLoc.getManager(); unsigned MacroInstSkipStart = 0, MacroInstSkipEnd = 0; if (DiagOpts && DiagOpts->MacroBacktraceLimit && !LastLoc.isFileID()) { // Compute the length of the macro-instantiation backtrace, so that we @@ -1051,7 +1120,8 @@ void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, unsigned Depth = 0; do { ++Depth; - Loc = LastLoc.getManager().getImmediateInstantiationRange(Loc).first; + Loc = skipToMacroArgInstantiation(SM, Loc); + Loc = getImmediateMacroCallerLoc(SM, Loc); } while (!Loc.isFileID()); if (Depth > DiagOpts->MacroBacktraceLimit) { diff --git a/lib/Lex/TokenLexer.cpp b/lib/Lex/TokenLexer.cpp index e2b002549a..fac6b7dc3e 100644 --- a/lib/Lex/TokenLexer.cpp +++ b/lib/Lex/TokenLexer.cpp @@ -231,9 +231,9 @@ void TokenLexer::ExpandFunctionArguments() { "Expected arg identifier to come from definition"); for (unsigned i = FirstResult, e = ResultToks.size(); i != e; ++i) { Token &Tok = ResultToks[i]; - Tok.setLocation(SM.createInstantiationLoc(Tok.getLocation(), - curInst, curInst, - Tok.getLength())); + Tok.setLocation(SM.createMacroArgInstantiationLoc(Tok.getLocation(), + curInst, + Tok.getLength())); } } diff --git a/test/Misc/caret-diags-macros.c b/test/Misc/caret-diags-macros.c index b922f33af0..a70361820f 100644 --- a/test/Misc/caret-diags-macros.c +++ b/test/Misc/caret-diags-macros.c @@ -5,11 +5,11 @@ void foo() { M1( M2); - // CHECK: :6:3: warning: expression result unused - // CHECK: :7:5: note: instantiated from: + // CHECK: :7:{{[0-9]+}}: warning: expression result unused + // CHECK: :4:{{[0-9]+}}: note: instantiated from: + // CHECK: :3:{{[0-9]+}}: note: instantiated from: } - #define A 1 #define B A #define C B @@ -21,7 +21,6 @@ void bar() { // CHECK: :13:11: note: instantiated from: } - // rdar://7597492 #define sprintf(str, A, B) \ __builtin___sprintf_chk (str, 0, 42, A, B) @@ -31,14 +30,57 @@ void baz(char *Msg) { } +// PR9279: comprehensive tests for multi-level macro back traces +#define macro_args1(x) x +#define macro_args2(x) macro_args1(x) +#define macro_args3(x) macro_args2(x) + +#define macro_many_args1(x, y, z) y +#define macro_many_args2(x, y, z) macro_many_args1(x, y, z) +#define macro_many_args3(x, y, z) macro_many_args2(x, y, z) -// PR9279 - Notes shouldn't print 'instantiated from' notes recursively. -#define N1(x) int arr[x] -#define N2(x) N1(x) -#define N3(x) N2(x) -N3(-1); +void test() { + macro_args3(1); + // CHECK: {{.*}}:43:15: warning: expression result unused + // Also check that the 'caret' printing agrees with the location here where + // its easy to FileCheck. + // CHECK-NEXT: macro_args3(1); + // CHECK-NEXT: ~~~~~~~~~~~~^~ + // CHECK: {{.*}}:36:36: note: instantiated from: + // CHECK: {{.*}}:35:36: note: instantiated from: + // CHECK: {{.*}}:34:24: note: instantiated from: -// CHECK: :39:1: error: 'arr' declared as an array with a negative size -// CHECK: :38:15: note: instantiated from: -// CHECK: :37:15: note: instantiated from: -// CHECK: :39:1: note: instantiated from: + macro_many_args3( + 1, + 2, + 3); + // CHECK: {{.*}}:55:5: warning: expression result unused + // CHECK: {{.*}}:40:55: note: instantiated from: + // CHECK: {{.*}}:39:55: note: instantiated from: + // CHECK: {{.*}}:38:35: note: instantiated from: + + macro_many_args3( + 1, + M2, + 3); + // CHECK: {{.*}}:64:5: warning: expression result unused + // CHECK: {{.*}}:4:12: note: instantiated from: + // CHECK: {{.*}}:40:55: note: instantiated from: + // CHECK: {{.*}}:39:55: note: instantiated from: + // CHECK: {{.*}}:38:35: note: instantiated from: + + macro_many_args3( + 1, + macro_args2(2), + 3); + // CHECK: {{.*}}:74:17: warning: expression result unused + // This caret location needs to be printed *inside* a different macro's + // arguments. + // CHECK-NEXT: macro_args2(2), + // CHECK-NEXT: ~~~~~~~~~~~~^~~ + // CHECK: {{.*}}:35:36: note: instantiated from: + // CHECK: {{.*}}:34:24: note: instantiated from: + // CHECK: {{.*}}:40:55: note: instantiated from: + // CHECK: {{.*}}:39:55: note: instantiated from: + // CHECK: {{.*}}:38:35: note: instantiated from: +} |