diff --git a/clang/docs/InternalsManual.html b/clang/docs/InternalsManual.html index 9250fd1dd777..dfd487aa45de 100644 --- a/clang/docs/InternalsManual.html +++ b/clang/docs/InternalsManual.html @@ -386,6 +386,63 @@ completely independent of how the diagnostic is formatted and in what language it is rendered.

+ +

Code Modification Hints

+ + +

In some cases, the front end emits diagnostics when it is clear +that some small change to the source code would fix the problem. For +example, a missing semicolon at the end of a statement or a use of +deprecated syntax that is easily rewritten into a more modern form. In +these cases, the front end should emit the diagnostic and recover +gracefully.

+ +

In these cases, the diagnostic can be annotation with a code +modification "hint" that describes how to modify the code referenced +by the diagnostic to fix the problem. For example, it might add the +missing semicolon at the end of the statement or rewrite the use of a +deprecated construct into something more palatable. Here is one such +example C++ front end, where we warn about the right-shift operator +changing meaning from C++98 to C++0x:

+ +
+test.cpp:3:7: warning: use of right-shift operator ('>>') in template argument will require parentheses in C++0x
+A<100 >> 2> *a;
+      ^
+  (       )
+
+ +

Here, the code modification hint is suggesting that parentheses be +added, and showing exactly where those parentheses would be inserted +into the source code. The code modification hints themselves describe +what changes to make to the source code in an abstract manner, which +the text diagnostic printer renders as a line of "insertions" below +the caret line. Other diagnostic +clients might choose to render the code differently (e.g., as +markup inline) or even give the user the ability to automatically fix +the problem.

+ +

All code modification hints are described by the +CodeModificationHint class, instances of which should be +attached to the diagnostic using the << operator in the same way +that highlighted source ranges and arguments are passed to the +diagnostic. Code modification hints can be created with one of three +constructors:

+ +
+
CodeModificationHint::CreateInsertion(Loc, Code)
+
Specifies that the given Code (a string) should be inserted + before the source location Loc.
+ +
CodeModificationHint::CreateRemoval(Range)
+
Specifies that the code in the given source Range + should be removed.
+ +
CodeModificationHint::CreateReplacement(Range, Code)
+
Specifies that the code in the given source Range + should be removed, and replaced with the given Code string.
+
+

The DiagnosticClient Interface

diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index 7204abbd2afa..6e8ef26f5cd3 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -75,7 +75,7 @@ namespace clang { /// introduction, removal, or modification of a particular (small!) /// amount of code will correct a compilation error. The compiler /// should also provide full recovery from such errors, such that -/// suppressing the diagnostic output can still result successful +/// suppressing the diagnostic output can still result in successful /// compilation. class CodeModificationHint { public: @@ -96,41 +96,34 @@ public: /// \brief Create a code modification hint that inserts the given /// code string at a specific location. - CodeModificationHint(SourceLocation InsertionLoc, const std::string &Code) - : RemoveRange(), InsertionLoc(InsertionLoc), CodeToInsert(Code) { } + static CodeModificationHint CreateInsertion(SourceLocation InsertionLoc, + const std::string &Code) { + CodeModificationHint Hint; + Hint.InsertionLoc = InsertionLoc; + Hint.CodeToInsert = Code; + return Hint; + } /// \brief Create a code modification hint that removes the given /// source range. - CodeModificationHint(SourceRange RemoveRange) - : RemoveRange(RemoveRange), InsertionLoc(), CodeToInsert() { } + static CodeModificationHint CreateRemoval(SourceRange RemoveRange) { + CodeModificationHint Hint; + Hint.RemoveRange = RemoveRange; + return Hint; + } /// \brief Create a code modification hint that replaces the given /// source range with the given code string. - CodeModificationHint(SourceRange RemoveRange, const std::string &Code) - : RemoveRange(RemoveRange), InsertionLoc(RemoveRange.getBegin()), - CodeToInsert(Code) { } + static CodeModificationHint CreateReplacement(SourceRange RemoveRange, + const std::string &Code) { + CodeModificationHint Hint; + Hint.RemoveRange = RemoveRange; + Hint.InsertionLoc = RemoveRange.getBegin(); + Hint.CodeToInsert = Code; + return Hint; + } }; -/// \brief Creates a code modification hint that inserts the given -/// string at a particular location in the source code. -inline CodeModificationHint -CodeInsertionHint(SourceLocation InsertionLoc, const std::string &Code) { - return CodeModificationHint(InsertionLoc, Code); -} - -/// \brief Creates a code modification hint that removes the given -/// source range. -inline CodeModificationHint CodeRemovalHint(SourceRange RemoveRange) { - return CodeModificationHint(RemoveRange); -} - -/// \brief Creates a code modification hint that replaces the given -/// source range with the given code string. -inline CodeModificationHint -CodeReplacementHint(SourceRange RemoveRange, const std::string &Code) { - return CodeModificationHint(RemoveRange, Code); -} - /// Diagnostic - This concrete class is used by the front-end to report /// problems and issues. It massages the diagnostics (e.g. handling things like /// "report warnings as errors" and passes them off to the DiagnosticClient for diff --git a/clang/include/clang/Basic/TokenKinds.h b/clang/include/clang/Basic/TokenKinds.h index 6a14244362c0..62a9e428bf21 100644 --- a/clang/include/clang/Basic/TokenKinds.h +++ b/clang/include/clang/Basic/TokenKinds.h @@ -43,8 +43,20 @@ enum ObjCKeywordKind { NUM_OBJC_KEYWORDS }; +/// \brief Determines the name of a token as used within the front end. +/// +/// The name of a token will be an internal name (such as "l_square") +/// and should not be used as part of diagnostic messages. const char *getTokenName(enum TokenKind Kind); -const char *getTokenSpelling(enum TokenKind Kind); + +/// \brief Determines the spelling of simple punctuation tokens like +/// '!' or '%', and returns NULL for literal and annotation tokens. +/// +/// This routine only retrieves the "simple" spelling of the token, +/// and will not produce any alternative spellings (e.g., a +/// digraph). For the actual spelling of a given Token, use +/// Preprocessor::getSpelling(). +const char *getTokenSimpleSpelling(enum TokenKind Kind); } // end namespace tok } // end namespace clang diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 9d99142480bf..ae7d3b6220da 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -476,7 +476,7 @@ public: /// copy). The caller is not allowed to modify the returned buffer pointer /// if an internal buffer is returned. unsigned getSpelling(const Token &Tok, const char *&Buffer) const; - + /// getSpellingOfSingleCharacterNumericConstant - Tok is a numeric constant /// with length 1, return the character. char getSpellingOfSingleCharacterNumericConstant(const Token &Tok) const { @@ -498,7 +498,19 @@ public: /// location provides a location of the instantiation point of the token. void CreateString(const char *Buf, unsigned Len, Token &Tok, SourceLocation SourceLoc = SourceLocation()); - + + /// \brief Computes the source location just past the end of the + /// token at this source location. + /// + /// This routine can be used to produce a source location that + /// points just past the end of the token referenced by \p Loc, and + /// is generally used when a diagnostic needs to point just after a + /// token where it expected something different that it received. If + /// the returned source location would not be meaningful (e.g., if + /// it points into a macro), this routine returns an invalid + /// source location. + SourceLocation getLocForEndOfToken(SourceLocation Loc); + /// DumpToken - Print the token to stderr, used for debugging. /// void DumpToken(const Token &Tok, bool DumpFlags = false) const; diff --git a/clang/lib/Basic/TokenKinds.cpp b/clang/lib/Basic/TokenKinds.cpp index 3d47863e8575..4afeaf01b776 100644 --- a/clang/lib/Basic/TokenKinds.cpp +++ b/clang/lib/Basic/TokenKinds.cpp @@ -28,9 +28,7 @@ const char *tok::getTokenName(enum TokenKind Kind) { return TokNames[Kind]; } -/// \brief Determines the spelling of simple punctuation tokens like -/// '!' or '%', and returns NULL for literal and annotation tokens. -const char *tok::getTokenSpelling(enum TokenKind Kind) { +const char *tok::getTokenSimpleSpelling(enum TokenKind Kind) { switch (Kind) { case tok::l_square: return "["; case tok::r_square: return "]"; diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index ccb179d1e4c2..26777d9170b9 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -265,7 +265,6 @@ unsigned Preprocessor::getSpelling(const Token &Tok, return OutBuf-Buffer; } - /// CreateString - Plop the specified string into a scratch buffer and return a /// location for it. If specified, the source location provides a source /// location for the token. @@ -321,6 +320,25 @@ SourceLocation Preprocessor::AdvanceToTokenCharacter(SourceLocation TokStart, return TokStart.getFileLocWithOffset(PhysOffset); } +/// \brief Computes the source location just past the end of the +/// token at this source location. +/// +/// This routine can be used to produce a source location that +/// points just past the end of the token referenced by \p Loc, and +/// is generally used when a diagnostic needs to point just after a +/// token where it expected something different that it received. If +/// the returned source location would not be meaningful (e.g., if +/// it points into a macro), this routine returns an invalid +/// source location. +SourceLocation Preprocessor::getLocForEndOfToken(SourceLocation Loc) { + if (Loc.isInvalid() || !Loc.isFileID()) + return SourceLocation(); + + unsigned Len = Lexer::MeasureTokenLength(Loc, getSourceManager()); + return AdvanceToTokenCharacter(Loc, Len); +} + + //===----------------------------------------------------------------------===// // Preprocessor Initialization Methods diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 0209d7b0ab59..2aeb182dbdf9 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -445,7 +445,8 @@ Parser::ParseTemplateIdAfterTemplateName(DeclTy *Template, ReplaceStr = "> > "; Diag(Tok.getLocation(), diag::err_two_right_angle_brackets_need_space) - << CodeReplacementHint(SourceRange(Tok.getLocation()), ReplaceStr); + << CodeModificationHint::CreateReplacement( + SourceRange(Tok.getLocation()), ReplaceStr); } Tok.setKind(tok::greater); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 134f477634dd..4398ef8ea9c9 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -76,18 +76,17 @@ DiagnosticBuilder Parser::Diag(const Token &Tok, unsigned DiagID) { /// \param ParenRange Source range enclosing code that should be parenthesized. void Parser::SuggestParentheses(SourceLocation Loc, unsigned DK, SourceRange ParenRange) { - if (!ParenRange.getEnd().isFileID()) { + SourceLocation EndLoc = PP.getLocForEndOfToken(ParenRange.getEnd()); + if (!ParenRange.getEnd().isFileID() || EndLoc.isInvalid()) { // We can't display the parentheses, so just dig the // warning/error and return. Diag(Loc, DK); return; } - unsigned Len = Lexer::MeasureTokenLength(ParenRange.getEnd(), - PP.getSourceManager()); Diag(Loc, DK) - << CodeInsertionHint(ParenRange.getBegin(), "(") - << CodeInsertionHint(ParenRange.getEnd().getFileLocWithOffset(Len), ")"); + << CodeModificationHint::CreateInsertion(ParenRange.getBegin(), "(") + << CodeModificationHint::CreateInsertion(EndLoc, ")"); } /// MatchRHSPunctuation - For punctuation with a LHS and RHS (e.g. '['/']'), @@ -131,14 +130,13 @@ bool Parser::ExpectAndConsume(tok::TokenKind ExpectedTok, unsigned DiagID, } const char *Spelling = 0; - if (PrevTokLocation.isValid() && PrevTokLocation.isFileID() && - (Spelling = tok::getTokenSpelling(ExpectedTok))) { + SourceLocation EndLoc = PP.getLocForEndOfToken(PrevTokLocation); + if (EndLoc.isValid() && + (Spelling = tok::getTokenSimpleSpelling(ExpectedTok))) { // Show what code to insert to fix this problem. - SourceLocation DiagLoc - = PrevTokLocation.getFileLocWithOffset(strlen(Spelling)); - Diag(DiagLoc, DiagID) + Diag(EndLoc, DiagID) << Msg - << CodeInsertionHint(DiagLoc, Spelling); + << CodeModificationHint::CreateInsertion(EndLoc, Spelling); } else Diag(Tok, DiagID) << Msg; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 97eb9be36ccc..5b2e89e8b0d4 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1617,7 +1617,7 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, // template<> headers. if (TemplateParameterLists.size() == 0) Diag(KWLoc, diag::err_template_spec_needs_header) - << CodeInsertionHint(KWLoc, "template<> "); + << CodeModificationHint::CreateInsertion(KWLoc, "template<> "); else { TemplateParameterList *TemplateParams = static_cast(*TemplateParameterLists.get());