diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Analysis/PathDiagnostic.cpp | 2 | ||||
-rw-r--r-- | lib/Basic/TokenKinds.cpp | 63 | ||||
-rw-r--r-- | lib/Driver/HTMLDiagnostics.cpp | 32 | ||||
-rw-r--r-- | lib/Driver/TextDiagnosticPrinter.cpp | 55 | ||||
-rw-r--r-- | lib/Parse/ParseExpr.cpp | 13 | ||||
-rw-r--r-- | lib/Parse/ParseTemplate.cpp | 10 | ||||
-rw-r--r-- | lib/Parse/Parser.cpp | 35 | ||||
-rw-r--r-- | lib/Rewrite/HTMLRewrite.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 2 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 5 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 10 |
11 files changed, 210 insertions, 20 deletions
diff --git a/lib/Analysis/PathDiagnostic.cpp b/lib/Analysis/PathDiagnostic.cpp index 4fa88ed595..50ff523b81 100644 --- a/lib/Analysis/PathDiagnostic.cpp +++ b/lib/Analysis/PathDiagnostic.cpp @@ -47,6 +47,8 @@ void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel, 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); diff --git a/lib/Basic/TokenKinds.cpp b/lib/Basic/TokenKinds.cpp index bde8a5598b..3d47863e85 100644 --- a/lib/Basic/TokenKinds.cpp +++ b/lib/Basic/TokenKinds.cpp @@ -27,3 +27,66 @@ const char *tok::getTokenName(enum TokenKind Kind) { 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; +} diff --git a/lib/Driver/HTMLDiagnostics.cpp b/lib/Driver/HTMLDiagnostics.cpp index 6a4bf2307f..8ab9b18ecc 100644 --- a/lib/Driver/HTMLDiagnostics.cpp +++ b/lib/Driver/HTMLDiagnostics.cpp @@ -51,7 +51,9 @@ public: 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); }; @@ -438,10 +440,33 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, 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(); @@ -473,6 +498,5 @@ void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID, SourceLocation E = InstantiationEnd.getFileLocWithOffset(EndColNo - OldEndColNo); - html::HighlightRange(R, InstantiationStart, E, - "<span class=\"mrange\">", "</span>"); + html::HighlightRange(R, InstantiationStart, E, HighlightStart, HighlightEnd); } diff --git a/lib/Driver/TextDiagnosticPrinter.cpp b/lib/Driver/TextDiagnosticPrinter.cpp index b5226f5131..8a7e40f13e 100644 --- a/lib/Driver/TextDiagnosticPrinter.cpp +++ b/lib/Driver/TextDiagnosticPrinter.cpp @@ -16,6 +16,7 @@ #include "clang/Lex/Lexer.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/SmallString.h" +#include <algorithm> using namespace clang; void TextDiagnosticPrinter:: @@ -104,7 +105,9 @@ void TextDiagnosticPrinter::HighlightRange(const SourceRange &R, 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 @@ -205,6 +208,36 @@ void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc, // 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'; + } } @@ -252,18 +285,30 @@ void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, // 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(); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index d0808a5f97..6c6842e0be 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -365,10 +365,19 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) { 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)); diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 65705e8d32..0209d7b0ab 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -439,8 +439,14 @@ Parser::ParseTemplateIdAfterTemplateName(DeclTy *Template, 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) { diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index a3ed027846..a67106c4d9 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -68,6 +68,28 @@ DiagnosticBuilder Parser::Diag(const Token &Tok, unsigned DiagID) { 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 @@ -108,7 +130,18 @@ bool Parser::ExpectAndConsume(tok::TokenKind ExpectedTok, unsigned DiagID, 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; diff --git a/lib/Rewrite/HTMLRewrite.cpp b/lib/Rewrite/HTMLRewrite.cpp index 0ef998deb1..692af80488 100644 --- a/lib/Rewrite/HTMLRewrite.cpp +++ b/lib/Rewrite/HTMLRewrite.cpp @@ -319,6 +319,9 @@ void html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, FileID FID, " .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" diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 25f2164ed0..68e0ce8f7d 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1036,6 +1036,8 @@ public: 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, diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 1f8cb80cd3..eec27cee83 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -110,6 +110,11 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc) { return false; } +SourceRange Sema::getExprRange(ExprTy *E) const { + Expr *Ex = (Expr *)E; + return Ex? Ex->getSourceRange() : SourceRange(); +} + //===----------------------------------------------------------------------===// // Standard Promotions and Conversions //===----------------------------------------------------------------------===// diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 0c60520142..f69f546177 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1615,12 +1615,10 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, // 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) { |