Clean up and document code modification hints.

llvm-svn: 65641
This commit is contained in:
Douglas Gregor 2009-02-27 17:53:17 +00:00
parent 7094c15d7e
commit 96977da72c
9 changed files with 137 additions and 48 deletions

View File

@ -386,6 +386,63 @@ completely independent of how the diagnostic is formatted and in what language
it is rendered.
</p>
<!-- ==================================================== -->
<h4 id="code-modification-hints">Code Modification Hints</h4>
<!-- ==================================================== -->
<p>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.</p>
<p>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:</p>
<pre>
test.cpp:3:7: warning: use of right-shift operator ('&gt;&gt;') in template argument will require parentheses in C++0x
A&lt;100 &gt;&gt; 2&gt; *a;
^
( )
</pre>
<p>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. <a href="#DiagnosticClient">Other diagnostic
clients</a> might choose to render the code differently (e.g., as
markup inline) or even give the user the ability to automatically fix
the problem.</p>
<p>All code modification hints are described by the
<code>CodeModificationHint</code> class, instances of which should be
attached to the diagnostic using the &lt;&lt; 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:</p>
<dl>
<dt><code>CodeModificationHint::CreateInsertion(Loc, Code)</code></dt>
<dd>Specifies that the given <code>Code</code> (a string) should be inserted
before the source location <code>Loc</code>.</dd>
<dt><code>CodeModificationHint::CreateRemoval(Range)</code></dt>
<dd>Specifies that the code in the given source <code>Range</code>
should be removed.</dd>
<dt><code>CodeModificationHint::CreateReplacement(Range, Code)</code></dt>
<dd>Specifies that the code in the given source <code>Range</code>
should be removed, and replaced with the given <code>Code</code> string.</dd>
</dl>
<!-- ============================================================= -->
<h4><a name="DiagnosticClient">The DiagnosticClient Interface</a></h4>
<!-- ============================================================= -->

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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 "]";

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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<TemplateParameterList*>(*TemplateParameterLists.get());