From 96977da72c7cce3109cd787f795e586771980562 Mon Sep 17 00:00:00 2001
From: Douglas Gregor
Date: Fri, 27 Feb 2009 17:53:17 +0000
Subject: [PATCH] Clean up and document code modification hints.
llvm-svn: 65641
---
clang/docs/InternalsManual.html | 57 ++++++++++++++++++++++++++
clang/include/clang/Basic/Diagnostic.h | 49 ++++++++++------------
clang/include/clang/Basic/TokenKinds.h | 14 ++++++-
clang/include/clang/Lex/Preprocessor.h | 16 +++++++-
clang/lib/Basic/TokenKinds.cpp | 4 +-
clang/lib/Lex/Preprocessor.cpp | 20 ++++++++-
clang/lib/Parse/ParseTemplate.cpp | 3 +-
clang/lib/Parse/Parser.cpp | 20 ++++-----
clang/lib/Sema/SemaTemplate.cpp | 2 +-
9 files changed, 137 insertions(+), 48 deletions(-)
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.
+
+
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());