private:
FullSourceLoc Pos;
std::string str;
+ std::vector<CodeModificationHint> CodeModificationHints;
DisplayHint Hint;
std::vector<SourceRange> ranges;
ranges.push_back(SourceRange(B,E));
}
+ void addCodeModificationHint(const CodeModificationHint& Hint) {
+ CodeModificationHints.push_back(Hint);
+ }
+
typedef const SourceRange* range_iterator;
range_iterator ranges_begin() const {
range_iterator ranges_end() const {
return ranges_begin() + ranges.size();
}
-
+
+ typedef const CodeModificationHint *code_modifications_iterator;
+
+ code_modifications_iterator code_modifications_begin() const {
+ return CodeModificationHints.empty()? 0 : &CodeModificationHints[0];
+ }
+
+ code_modifications_iterator code_modifications_end() const {
+ return CodeModificationHints.empty()? 0
+ : &CodeModificationHints[0] + CodeModificationHints.size();
+ }
+
const SourceManager& getSourceManager() const {
return Pos.getManager();
}
};
}
+/// \brief Annotates a diagnostic with some code that should be
+/// inserted, removed, or replaced to fix the problem.
+///
+/// This kind of hint should be used when we are certain that the
+/// 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
+/// compilation.
+class CodeModificationHint {
+public:
+ /// \brief Tokens that should be removed to correct the error.
+ SourceRange RemoveRange;
+
+ /// \brief The location at which we should insert code to correct
+ /// the error.
+ SourceLocation InsertionLoc;
+
+ /// \brief The actual code to insert at the insertion location, as a
+ /// string.
+ std::string CodeToInsert;
+
+ /// \brief Empty code modification hint, indicating that no code
+ /// modification is known.
+ CodeModificationHint() : RemoveRange(), InsertionLoc() { }
+
+ /// \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) { }
+
+ /// \brief Create a code modification hint that removes the given
+ /// source range.
+ CodeModificationHint(SourceRange RemoveRange)
+ : RemoveRange(RemoveRange), InsertionLoc(), CodeToInsert() { }
+
+ /// \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) { }
+};
+
+/// \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
signed char NumDiagArgs;
/// NumRanges - This is the number of ranges in the DiagRanges array.
unsigned char NumDiagRanges;
+ /// \brief The number of code modifications hints in the
+ /// CodeModificationHints array.
+ unsigned char NumCodeModificationHints;
/// DiagArgumentsKind - This is an array of ArgumentKind::ArgumentKind enum
/// values, with one for each argument. This specifies whether the argument
/// only support 10 ranges, could easily be extended if needed.
const SourceRange *DiagRanges[10];
+ enum { MaxCodeModificationHints = 3 };
+
+ /// CodeModificationHints - If valid, provides a hint with some code
+ /// to insert, remove, or modify at a particular position.
+ CodeModificationHint CodeModificationHints[MaxCodeModificationHints];
+
/// ProcessDiag - This is the method used to report a diagnostic that is
/// finally fully formed.
void ProcessDiag();
/// for example.
class DiagnosticBuilder {
mutable Diagnostic *DiagObj;
- mutable unsigned NumArgs, NumRanges;
+ mutable unsigned NumArgs, NumRanges, NumCodeModificationHints;
void operator=(const DiagnosticBuilder&); // DO NOT IMPLEMENT
friend class Diagnostic;
explicit DiagnosticBuilder(Diagnostic *diagObj)
- : DiagObj(diagObj), NumArgs(0), NumRanges(0) {}
+ : DiagObj(diagObj), NumArgs(0), NumRanges(0),
+ NumCodeModificationHints(0) {}
public:
/// Copy constructor. When copied, this "takes" the diagnostic info from the
// the Diagnostic object.
DiagObj->NumDiagArgs = NumArgs;
DiagObj->NumDiagRanges = NumRanges;
+ DiagObj->NumCodeModificationHints = NumCodeModificationHints;
// Process the diagnostic, sending the accumulated information to the
// DiagnosticClient.
"Too many arguments to diagnostic!");
DiagObj->DiagRanges[NumRanges++] = &R;
}
+
+ void AddCodeModificationHint(const CodeModificationHint &Hint) const {
+ assert(NumCodeModificationHints < Diagnostic::MaxCodeModificationHints &&
+ "Too many code modification hints!");
+ DiagObj->CodeModificationHints[NumCodeModificationHints++] = Hint;
+ }
};
inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
DB.AddSourceRange(R);
return DB;
}
+
+inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
+ const CodeModificationHint &Hint) {
+ DB.AddCodeModificationHint(Hint);
+ return DB;
+}
/// Report - Issue the message to the client. DiagID is a member of the
//===----------------------------------------------------------------------===//
/// DiagnosticInfo - This is a little helper class (which is basically a smart
-/// pointer that forward info from Diagnostic) that allows clients ot enquire
+/// pointer that forward info from Diagnostic) that allows clients to enquire
/// about the currently in-flight diagnostic.
class DiagnosticInfo {
const Diagnostic *DiagObj;
return *DiagObj->DiagRanges[Idx];
}
-
+ unsigned getNumCodeModificationHints() const {
+ return DiagObj->NumCodeModificationHints;
+ }
+
+ const CodeModificationHint &getCodeModificationHint(unsigned Idx) const {
+ return DiagObj->CodeModificationHints[Idx];
+ }
+
+ const CodeModificationHint *getCodeModificationHints() const {
+ return DiagObj->NumCodeModificationHints?
+ &DiagObj->CodeModificationHints[0] : 0;
+ }
+
/// FormatDiagnostic - Format this diagnostic into a string, substituting the
/// formal arguments into the %0 slots. The result is appended onto the Str
/// array.
void FormatDiagnostic(llvm::SmallVectorImpl<char> &OutStr) const;
};
-
/// DiagnosticClient - This is an abstract interface implemented by clients of
/// the front-end, which formats and prints fully processed diagnostics.
class DiagnosticClient {
"expected '<' after 'template %0' in nested name specifier")
DIAG(err_two_right_angle_brackets_need_space, ERROR,
"a space is required between consecutive right angle brackets (use '> >')")
+DIAG(warn_cxx0x_right_shift_in_template_arg, WARNING,
+ "use of right-shift operator ('>>') in template argument will require "
+ "parentheses in C++0x")
// Language specific pragmas
};
const char *getTokenName(enum TokenKind Kind);
+const char *getTokenSpelling(enum TokenKind Kind);
} // end namespace tok
} // end namespace clang
void EmitCaretDiagnostic(SourceLocation Loc,
SourceRange *Ranges, unsigned NumRanges,
- SourceManager &SM);
+ SourceManager &SM,
+ const CodeModificationHint *Hints = 0,
+ unsigned NumHints = 0);
virtual void HandleDiagnostic(Diagnostic::Level DiagLevel,
const DiagnosticInfo &Info);
// Primary Expressions.
+ /// \brief Retrieve the source range that corresponds to the given
+ /// expression.
+ virtual SourceRange getExprRange(ExprTy *E) const {
+ return SourceRange();
+ }
+
/// ActOnIdentifierExpr - Parse an identifier in expression context.
/// 'HasTrailingLParen' indicates whether or not the identifier has a '('
/// token immediately after it.
/// that this is valid.
Token Tok;
+ // PrevTokLocation - The location of the token we previously
+ // consumed. This token is used for diagnostics where we expected to
+ // see a token following another token (e.g., the ';' at the end of
+ // a statement).
+ SourceLocation PrevTokLocation;
+
unsigned short ParenCount, BracketCount, BraceCount;
/// Actions - These are the callbacks we invoke as we parse various constructs
assert(!isTokenStringLiteral() && !isTokenParen() && !isTokenBracket() &&
!isTokenBrace() &&
"Should consume special tokens with Consume*Token");
- SourceLocation L = Tok.getLocation();
+ PrevTokLocation = Tok.getLocation();
PP.Lex(Tok);
- return L;
+ return PrevTokLocation;
}
/// ConsumeAnyToken - Dispatch to the right Consume* method based on the
++ParenCount;
else if (ParenCount)
--ParenCount; // Don't let unbalanced )'s drive the count negative.
- SourceLocation L = Tok.getLocation();
+ PrevTokLocation = Tok.getLocation();
PP.Lex(Tok);
- return L;
+ return PrevTokLocation;
}
/// ConsumeBracket - This consume method keeps the bracket count up-to-date.
else if (BracketCount)
--BracketCount; // Don't let unbalanced ]'s drive the count negative.
- SourceLocation L = Tok.getLocation();
+ PrevTokLocation = Tok.getLocation();
PP.Lex(Tok);
- return L;
+ return PrevTokLocation;
}
/// ConsumeBrace - This consume method keeps the brace count up-to-date.
else if (BraceCount)
--BraceCount; // Don't let unbalanced }'s drive the count negative.
- SourceLocation L = Tok.getLocation();
+ PrevTokLocation = Tok.getLocation();
PP.Lex(Tok);
- return L;
+ return PrevTokLocation;
}
/// ConsumeStringToken - Consume the current 'peek token', lexing a new one
SourceLocation ConsumeStringToken() {
assert(isTokenStringLiteral() &&
"Should only consume string literals with this method");
- SourceLocation L = Tok.getLocation();
+ PrevTokLocation = Tok.getLocation();
PP.Lex(Tok);
- return L;
+ return PrevTokLocation;
}
/// GetLookAheadToken - This peeks ahead N tokens and returns that token
DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID);
DiagnosticBuilder Diag(const Token &Tok, unsigned DiagID);
+ void SuggestParentheses(SourceLocation Loc, unsigned DK,
+ SourceRange ParenRange);
+
/// SkipUntil - Read tokens until we get to the specified token, then consume
/// it (unless DontConsume is true). Because we cannot guarantee that the
/// token will ever occur, this skips to the next token, or to some likely
for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i)
P->addRange(Info.getRange(i));
+ for (unsigned i = 0, e = Info.getNumCodeModificationHints(); i != e; ++i)
+ P->addCodeModificationHint(Info.getCodeModificationHint(i));
D->push_front(P);
HandlePathDiagnostic(D);
assert(Kind < tok::NUM_TOKENS);
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) {
+ switch (Kind) {
+ case tok::l_square: return "[";
+ case tok::r_square: return "]";
+ case tok::l_paren: return "(";
+ case tok::r_paren: return ")";
+ case tok::l_brace: return "{";
+ case tok::r_brace: return "}";
+ case tok::period: return ".";
+ case tok::ellipsis: return "...";
+ case tok::amp: return "&";
+ case tok::ampamp: return "&&";
+ case tok::ampequal: return "&=";
+ case tok::star: return "*";
+ case tok::starequal: return "*=";
+ case tok::plus: return "+";
+ case tok::plusplus: return "++";
+ case tok::plusequal: return "+=";
+ case tok::minus: return "-";
+ case tok::arrow: return "->";
+ case tok::minusminus: return "--";
+ case tok::minusequal: return "-=";
+ case tok::tilde: return "~";
+ case tok::exclaim: return "!";
+ case tok::exclaimequal: return "!=";
+ case tok::slash: return "/";
+ case tok::slashequal: return "/=";
+ case tok::percent: return "%";
+ case tok::percentequal: return "%=";
+ case tok::less: return "<";
+ case tok::lessless: return "<<";
+ case tok::lessequal: return "<=";
+ case tok::lesslessequal: return "<<=";
+ case tok::greater: return ">";
+ case tok::greatergreater: return ">>";
+ case tok::greaterequal: return ">=";
+ case tok::greatergreaterequal: return ">>=";
+ case tok::caret: return "^";
+ case tok::caretequal: return "^=";
+ case tok::pipe: return "|";
+ case tok::pipepipe: return "||";
+ case tok::pipeequal: return "|=";
+ case tok::question: return "?";
+ case tok::colon: return ":";
+ case tok::semi: return ";";
+ case tok::equal: return "=";
+ case tok::equalequal: return "==";
+ case tok::comma: return ",";
+ case tok::hash: return "#";
+ case tok::hashhash: return "##";
+ case tok::hashat: return "#@";
+ case tok::periodstar: return ".*";
+ case tok::arrowstar: return "->*";
+ case tok::coloncolon: return "::";
+ case tok::at: return "@";
+ default: break;
+ }
+
+ return 0;
+}
void HandlePiece(Rewriter& R, FileID BugFileID,
const PathDiagnosticPiece& P, unsigned num, unsigned max);
- void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range);
+ void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range,
+ const char *HighlightStart = "<span class=\"mrange\">",
+ const char *HighlightEnd = "</span>");
void ReportDiag(const PathDiagnostic& D);
};
for (const SourceRange *I = P.ranges_begin(), *E = P.ranges_end();
I != E; ++I)
HighlightRange(R, LPosInfo.first, *I);
+
+#if 0
+ // If there is a code insertion hint, insert that code.
+ // FIXME: This code is disabled because it seems to mangle the HTML
+ // output. I'm leaving it here because it's generally the right idea,
+ // but needs some help from someone more familiar with the rewriter.
+ for (const CodeModificationHint *Hint = P.code_modifications_begin(),
+ *HintEnd = P.code_modifications_end();
+ Hint != HintEnd; ++Hint) {
+ if (Hint->RemoveRange.isValid()) {
+ HighlightRange(R, LPosInfo.first, Hint->RemoveRange,
+ "<span class=\"CodeRemovalHint\">", "</span>");
+ }
+ if (Hint->InsertionLoc.isValid()) {
+ std::string EscapedCode = html::EscapeText(Hint->CodeToInsert, true);
+ EscapedCode = "<span class=\"CodeInsertionHint\">" + EscapedCode
+ + "</span>";
+ R.InsertStrBefore(Hint->InsertionLoc, EscapedCode);
+ }
+ }
+#endif
}
void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
- SourceRange Range) {
+ SourceRange Range,
+ const char *HighlightStart,
+ const char *HighlightEnd) {
SourceManager& SM = R.getSourceMgr();
SourceLocation E =
InstantiationEnd.getFileLocWithOffset(EndColNo - OldEndColNo);
- html::HighlightRange(R, InstantiationStart, E,
- "<span class=\"mrange\">", "</span>");
+ html::HighlightRange(R, InstantiationStart, E, HighlightStart, HighlightEnd);
}
#include "clang/Lex/Lexer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/SmallString.h"
+#include <algorithm>
using namespace clang;
void TextDiagnosticPrinter::
void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc,
SourceRange *Ranges,
unsigned NumRanges,
- SourceManager &SM) {
+ SourceManager &SM,
+ const CodeModificationHint *Hints,
+ unsigned NumHints) {
assert(!Loc.isInvalid() && "must have a valid source location here");
// We always emit diagnostics about the instantiation points, not the spelling
// Emit what we have computed.
OS << SourceLine << '\n';
OS << CaretLine << '\n';
+
+ if (NumHints) {
+ std::string InsertionLine;
+ for (const CodeModificationHint *Hint = Hints,
+ *LastHint = Hints + NumHints;
+ Hint != LastHint; ++Hint) {
+ if (Hint->InsertionLoc.isValid()) {
+ // We have an insertion hint. Determine whether the inserted
+ // code is on the same line as the caret.
+ std::pair<FileID, unsigned> HintLocInfo
+ = SM.getDecomposedLoc(Hint->InsertionLoc);
+ if (SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) ==
+ SM.getLineNumber(FID, FileOffset)) {
+ // Insert the new code into the line just below the code
+ // that the user wrote.
+ unsigned HintColNo
+ = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second);
+ unsigned LastColumnModified
+ = HintColNo - 1 + Hint->CodeToInsert.size();
+ if (LastColumnModified > InsertionLine.size())
+ InsertionLine.resize(LastColumnModified, ' ');
+ std::copy(Hint->CodeToInsert.begin(), Hint->CodeToInsert.end(),
+ InsertionLine.begin() + HintColNo - 1);
+ }
+ }
+ }
+
+ if (!InsertionLine.empty())
+ OS << InsertionLine << '\n';
+ }
}
// diagnostic, or if the diagnostic has ranges. We don't want to emit the
// same caret multiple times if one loc has multiple diagnostics.
if (CaretDiagnostics && Info.getLocation().isValid() &&
- ((LastLoc != Info.getLocation()) || Info.getNumRanges())) {
+ ((LastLoc != Info.getLocation()) || Info.getNumRanges() ||
+ Info.getNumCodeModificationHints())) {
// Cache the LastLoc, it allows us to omit duplicate source/caret spewage.
LastLoc = Info.getLocation();
// Get the ranges into a local array we can hack on.
- SourceRange Ranges[10];
+ SourceRange Ranges[20];
unsigned NumRanges = Info.getNumRanges();
- assert(NumRanges < 10 && "Out of space");
+ assert(NumRanges < 20 && "Out of space");
for (unsigned i = 0; i != NumRanges; ++i)
Ranges[i] = Info.getRange(i);
- EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager());
+ unsigned NumHints = Info.getNumCodeModificationHints();
+ for (unsigned idx = 0; idx < NumHints; ++idx) {
+ const CodeModificationHint &Hint = Info.getCodeModificationHint(idx);
+ if (Hint.RemoveRange.isValid()) {
+ assert(NumRanges < 20 && "Out of space");
+ Ranges[NumRanges++] = Hint.RemoveRange;
+ }
+ }
+
+ EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(),
+ Info.getCodeModificationHints(),
+ Info.getNumCodeModificationHints());
}
OS.flush();
if (!LHS.isInvalid()) {
// Combine the LHS and RHS into the LHS (e.g. build AST).
- if (TernaryMiddle.isInvalid())
+ if (TernaryMiddle.isInvalid()) {
+ // If we're using '>>' as an operator within a template
+ // argument list (in C++98), suggest the addition of
+ // parentheses so that the code remains well-formed in C++0x.
+ if (!GreaterThanIsOperator && OpToken.is(tok::greatergreater))
+ SuggestParentheses(OpToken.getLocation(),
+ diag::warn_cxx0x_right_shift_in_template_arg,
+ SourceRange(Actions.getExprRange(LHS.get()).getBegin(),
+ Actions.getExprRange(RHS.get()).getEnd()));
+
LHS = Actions.ActOnBinOp(CurScope, OpToken.getLocation(),
OpToken.getKind(), move(LHS), move(RHS));
- else
+ } else
LHS = Actions.ActOnConditionalOp(OpToken.getLocation(), ColonLoc,
move(LHS), move(TernaryMiddle),
move(RHS));
RAngleLoc = Tok.getLocation();
if (Tok.is(tok::greatergreater)) {
- if (!getLang().CPlusPlus0x)
- Diag(Tok.getLocation(), diag::err_two_right_angle_brackets_need_space);
+ if (!getLang().CPlusPlus0x) {
+ const char *ReplaceStr = "> >";
+ if (NextToken().is(tok::greater) || NextToken().is(tok::greatergreater))
+ ReplaceStr = "> > ";
+
+ Diag(Tok.getLocation(), diag::err_two_right_angle_brackets_need_space)
+ << CodeReplacementHint(SourceRange(Tok.getLocation()), ReplaceStr);
+ }
Tok.setKind(tok::greater);
if (!ConsumeLastToken) {
return Diag(Tok.getLocation(), DiagID);
}
+/// \brief Emits a diagnostic suggesting parentheses surrounding a
+/// given range.
+///
+/// \param Loc The location where we'll emit the diagnostic.
+/// \param Loc The kind of diagnostic to emit.
+/// \param ParenRange Source range enclosing code that should be parenthesized.
+void Parser::SuggestParentheses(SourceLocation Loc, unsigned DK,
+ SourceRange ParenRange) {
+ if (!ParenRange.getEnd().isFileID()) {
+ // 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), ")");
+}
+
/// MatchRHSPunctuation - For punctuation with a LHS and RHS (e.g. '['/']'),
/// this helper function matches and consumes the specified RHS token if
/// present. If not present, it emits the specified diagnostic indicating
return false;
}
- Diag(Tok, DiagID) << Msg;
+ const char *Spelling = 0;
+ if (PrevTokLocation.isValid() && PrevTokLocation.isFileID() &&
+ (Spelling = tok::getTokenSpelling(ExpectedTok))) {
+ // Show what code to insert to fix this problem.
+ SourceLocation DiagLoc
+ = PrevTokLocation.getFileLocWithOffset(strlen(Spelling));
+ Diag(DiagLoc, DiagID)
+ << Msg
+ << CodeInsertionHint(DiagLoc, Spelling);
+ } else
+ Diag(Tok, DiagID) << Msg;
+
if (SkipToTok != tok::unknown)
SkipUntil(SkipToTok);
return true;
" .mrange { background-color:#dfddf3 }\n"
" .mrange { border-bottom:1px solid #6F9DBE }\n"
" .PathIndex { font-weight: bold }\n"
+ " .CodeInsertionHint { font-weight: bold; background-color: #10dd10 }\n"
+ " .CodeRemovalHint { background-color:#de1010 }\n"
+ " .CodeRemovalHint { border-bottom:1px solid #6F9DBE }\n"
" table.simpletable {\n"
" padding: 5px;\n"
" font-size:12pt;\n"
bool DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc);
// Primary Expressions.
+ virtual SourceRange getExprRange(ExprTy *E) const;
+
virtual OwningExprResult ActOnIdentifierExpr(Scope *S, SourceLocation Loc,
IdentifierInfo &II,
bool HasTrailingLParen,
return false;
}
+SourceRange Sema::getExprRange(ExprTy *E) const {
+ Expr *Ex = (Expr *)E;
+ return Ex? Ex->getSourceRange() : SourceRange();
+}
+
//===----------------------------------------------------------------------===//
// Standard Promotions and Conversions
//===----------------------------------------------------------------------===//
// FIXME: Once we have member templates, we'll need to check
// C++ [temp.expl.spec]p17-18, where we could have multiple levels of
// template<> headers.
- if (TemplateParameterLists.size() == 0) {
- // FIXME: It would be very nifty if we could introduce some kind
- // of "code insertion hint" that could show the code that needs to
- // be added.
- Diag(KWLoc, diag::err_template_spec_needs_header);
- } else {
+ if (TemplateParameterLists.size() == 0)
+ Diag(KWLoc, diag::err_template_spec_needs_header)
+ << CodeInsertionHint(KWLoc, "template<> ");
+ else {
TemplateParameterList *TemplateParams
= static_cast<TemplateParameterList*>(*TemplateParameterLists.get());
if (TemplateParameterLists.size() > 1) {
using namespace ! ; // expected-error{{expected namespace name}}
using namespace A ; // expected-error{{expected namespace name}}
-using namespace ::A // expected-error{{expected namespace name}}
- B ; // expected-error{{expected ';' after namespace name}}
+using namespace ::A // expected-error{{expected namespace name}} \
+ // expected-error{{expected ';' after namespace name}}
+ B ;
void test_nslookup() {
int B;
struct forward; // expected-note{{forward declaration of 'struct forward'}}
void x(struct forward* x) {switch(x->a) {}} // expected-error {{incomplete definition of type}}
+// PR3410
+void foo() {
+ int X;
+ X = 4 // expected-error{{expected ';' after expression}}
+}
>> *x3; // expected-error{{a space is required between consecutive right angle brackets (use '> >')}}
Y<(1 >> 2)> *y1;
-Y<1 >> 2> *y2;
-// FIXME: when we get a -Wc++0x mode, warn about the use above
+Y<1 >> 2> *y2; // expected-warning{{use of right-shift operator ('>>') in template argument will require parentheses in C++0x}}
A<int> *a2; // expected-error{{template argument for non-type template parameter must be an expression}}
-A<1 >> 2> *a3;
+A<1 >> 2> *a3; // expected-warning{{use of right-shift operator ('>>') in template argument will require parentheses in C++0x}}
// C++ [temp.arg.nontype]p5:
A<A> *a4; // expected-error{{must have an integral or enumeration type}} \