diff options
-rw-r--r-- | include/clang/AST/ASTContext.h | 6 | ||||
-rw-r--r-- | include/clang/AST/CommentSema.h | 6 | ||||
-rw-r--r-- | include/clang/AST/RawCommentList.h | 4 | ||||
-rw-r--r-- | include/clang/Lex/Preprocessor.h | 27 | ||||
-rw-r--r-- | lib/AST/ASTContext.cpp | 8 | ||||
-rw-r--r-- | lib/AST/CommentSema.cpp | 24 | ||||
-rw-r--r-- | lib/AST/RawCommentList.cpp | 4 | ||||
-rw-r--r-- | lib/Lex/Preprocessor.cpp | 33 | ||||
-rw-r--r-- | lib/Sema/AnalysisBasedWarnings.cpp | 66 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 2 | ||||
-rw-r--r-- | test/Sema/warn-documentation-fixits.cpp | 7 | ||||
-rw-r--r-- | tools/libclang/CIndex.cpp | 2 | ||||
-rw-r--r-- | unittests/AST/CommentParser.cpp | 2 |
13 files changed, 120 insertions, 71 deletions
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 1d037ac5e0..f52ee0470a 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -532,7 +532,11 @@ public: /// Return parsed documentation comment attached to a given declaration. /// Returns NULL if no comment is attached. - comments::FullComment *getCommentForDecl(const Decl *D) const; + /// + /// \param PP the Preprocessor used with this TU. Could be NULL if + /// preprocessor is not available. + comments::FullComment *getCommentForDecl(const Decl *D, + const Preprocessor *PP) const; private: mutable comments::CommandTraits CommentCommandTraits; diff --git a/include/clang/AST/CommentSema.h b/include/clang/AST/CommentSema.h index 2c4efc307c..0340b3cfd5 100644 --- a/include/clang/AST/CommentSema.h +++ b/include/clang/AST/CommentSema.h @@ -25,6 +25,7 @@ namespace clang { class Decl; class SourceMgr; +class Preprocessor; namespace comments { class CommandTraits; @@ -43,6 +44,8 @@ class Sema { CommandTraits &Traits; + const Preprocessor *PP; + /// Information about the declaration this comment is attached to. DeclInfo *ThisDeclInfo; @@ -68,7 +71,8 @@ class Sema { public: Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr, - DiagnosticsEngine &Diags, CommandTraits &Traits); + DiagnosticsEngine &Diags, CommandTraits &Traits, + const Preprocessor *PP); void setDecl(const Decl *D); diff --git a/include/clang/AST/RawCommentList.h b/include/clang/AST/RawCommentList.h index 1778ccabb5..3a8b2183a5 100644 --- a/include/clang/AST/RawCommentList.h +++ b/include/clang/AST/RawCommentList.h @@ -18,6 +18,7 @@ namespace clang { class ASTContext; class ASTReader; class Decl; +class Preprocessor; namespace comments { class FullComment; @@ -114,7 +115,8 @@ public: } /// Parse the comment, assuming it is attached to decl \c D. - comments::FullComment *parse(const ASTContext &Context, const Decl *D) const; + comments::FullComment *parse(const ASTContext &Context, + const Preprocessor *PP, const Decl *D) const; private: SourceRange Range; diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index c957906b03..03ff02798b 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -55,6 +55,27 @@ class DirectoryLookup; class PreprocessingRecord; class ModuleLoader; +/// \brief Stores token information for comparing actual tokens with +/// predefined values. Only handles simple tokens and identifiers. +class TokenValue { + tok::TokenKind Kind; + IdentifierInfo *II; + +public: + TokenValue(tok::TokenKind Kind) : Kind(Kind), II(0) { + assert(Kind != tok::raw_identifier && "Raw identifiers are not supported."); + assert(Kind != tok::identifier && + "Identifiers should be created by TokenValue(IdentifierInfo *)"); + assert(!tok::isLiteral(Kind) && "Literals are not supported."); + assert(!tok::isAnnotation(Kind) && "Annotations are not supported."); + } + TokenValue(IdentifierInfo *II) : Kind(tok::identifier), II(II) {} + bool operator==(const Token &Tok) const { + return Tok.getKind() == Kind && + (!II || II == Tok.getIdentifierInfo()); + } +}; + /// Preprocessor - This object engages in a tight little dance with the lexer to /// efficiently preprocess tokens. Lexers know only about tokens within a /// single source file, and don't know anything about preprocessor-level issues @@ -491,6 +512,12 @@ public: macro_iterator macro_begin(bool IncludeExternalMacros = true) const; macro_iterator macro_end(bool IncludeExternalMacros = true) const; + /// \brief Return the name of the macro defined before \p Loc that has + /// spelling \p Tokens. If there are multiple macros with same spelling, + /// return the last one defined. + StringRef getLastMacroWithSpelling(SourceLocation Loc, + ArrayRef<TokenValue> Tokens) const; + const std::string &getPredefines() const { return Predefines; } /// setPredefines - Set the predefines for this Preprocessor. These /// predefines are automatically injected when parsing the main file. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index f19a2aaaf7..83f3f9ca11 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -355,7 +355,9 @@ const RawComment *ASTContext::getRawCommentForAnyRedecl( return RC; } -comments::FullComment *ASTContext::getCommentForDecl(const Decl *D) const { +comments::FullComment *ASTContext::getCommentForDecl( + const Decl *D, + const Preprocessor *PP) const { D = adjustDeclToTemplate(D); const Decl *Canonical = D->getCanonicalDecl(); llvm::DenseMap<const Decl *, comments::FullComment *>::iterator Pos = @@ -373,9 +375,9 @@ comments::FullComment *ASTContext::getCommentForDecl(const Decl *D) const { // because comments can contain references to parameter names which can be // different across redeclarations. if (D != OriginalDecl) - return getCommentForDecl(OriginalDecl); + return getCommentForDecl(OriginalDecl, PP); - comments::FullComment *FC = RC->parse(*this, D); + comments::FullComment *FC = RC->parse(*this, PP, D); ParsedComments[Canonical] = FC; return FC; } diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp index 4f9f1f241c..dd746ad119 100644 --- a/lib/AST/CommentSema.cpp +++ b/lib/AST/CommentSema.cpp @@ -13,7 +13,9 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" #include "clang/Basic/SourceManager.h" +#include "clang/Lex/Preprocessor.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/SmallString.h" namespace clang { namespace comments { @@ -23,9 +25,10 @@ namespace { } // unnamed namespace Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr, - DiagnosticsEngine &Diags, CommandTraits &Traits) : + DiagnosticsEngine &Diags, CommandTraits &Traits, + const Preprocessor *PP) : Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits), - ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) { + PP(PP), ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) { } void Sema::setDecl(const Decl *D) { @@ -527,10 +530,25 @@ void Sema::checkDeprecatedCommand(const BlockCommandComment *Command) { FD->doesThisDeclarationHaveABody()) return; + StringRef AttributeSpelling = "__attribute__((deprecated))"; + if (PP) { + TokenValue Tokens[] = { + tok::kw___attribute, tok::l_paren, tok::l_paren, + PP->getIdentifierInfo("deprecated"), + tok::r_paren, tok::r_paren + }; + StringRef MacroName = PP->getLastMacroWithSpelling(FD->getLocation(), + Tokens); + if (!MacroName.empty()) + AttributeSpelling = MacroName; + } + + SmallString<64> TextToInsert(" "); + TextToInsert += AttributeSpelling; Diag(FD->getLocEnd(), diag::note_add_deprecation_attr) << FixItHint::CreateInsertion(FD->getLocEnd().getLocWithOffset(1), - " __attribute__((deprecated))"); + TextToInsert); } } diff --git a/lib/AST/RawCommentList.cpp b/lib/AST/RawCommentList.cpp index 28ef6112b8..80b627293e 100644 --- a/lib/AST/RawCommentList.cpp +++ b/lib/AST/RawCommentList.cpp @@ -159,6 +159,7 @@ const char *RawComment::extractBriefText(const ASTContext &Context) const { } comments::FullComment *RawComment::parse(const ASTContext &Context, + const Preprocessor *PP, const Decl *D) const { // Make sure that RawText is valid. getRawText(Context.getSourceManager()); @@ -168,7 +169,8 @@ comments::FullComment *RawComment::parse(const ASTContext &Context, RawText.begin(), RawText.end()); comments::Sema S(Context.getAllocator(), Context.getSourceManager(), Context.getDiagnostics(), - Context.getCommentCommandTraits()); + Context.getCommentCommandTraits(), + PP); S.setDecl(D); comments::Parser P(L, S, Context.getAllocator(), Context.getSourceManager(), Context.getDiagnostics(), diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index 872cda390a..a0fc265b6b 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -285,6 +285,39 @@ Preprocessor::macro_end(bool IncludeExternalMacros) const { return Macros.end(); } +/// \brief Compares macro tokens with a specified token value sequence. +static bool MacroDefinitionEquals(const MacroInfo *MI, + llvm::ArrayRef<TokenValue> Tokens) { + return Tokens.size() == MI->getNumTokens() && + std::equal(Tokens.begin(), Tokens.end(), MI->tokens_begin()); +} + +StringRef Preprocessor::getLastMacroWithSpelling( + SourceLocation Loc, + ArrayRef<TokenValue> Tokens) const { + SourceLocation BestLocation; + StringRef BestSpelling; + for (Preprocessor::macro_iterator I = macro_begin(), E = macro_end(); + I != E; ++I) { + if (!I->second->isObjectLike()) + continue; + const MacroInfo *MI = I->second->findDefinitionAtLoc(Loc, SourceMgr); + if (!MI) + continue; + if (!MacroDefinitionEquals(MI, Tokens)) + continue; + SourceLocation Location = I->second->getDefinitionLoc(); + // Choose the macro defined latest. + if (BestLocation.isInvalid() || + (Location.isValid() && + SourceMgr.isBeforeInTranslationUnit(BestLocation, Location))) { + BestLocation = Location; + BestSpelling = I->first->getName(); + } + } + return BestSpelling; +} + void Preprocessor::recomputeCurLexerKind() { if (CurLexer) CurLexerKind = CLK_Lexer; diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index ad5c739037..67d290b73c 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -41,6 +41,7 @@ #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" @@ -679,61 +680,6 @@ static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD, return true; } -/// \brief Stores token information for comparing actual tokens with -/// predefined values. Only handles simple tokens and identifiers. -class TokenValue { - tok::TokenKind Kind; - IdentifierInfo *II; - -public: - TokenValue(tok::TokenKind Kind) : Kind(Kind), II(0) { - assert(Kind != tok::raw_identifier && "Raw identifiers are not supported."); - assert(Kind != tok::identifier && - "Identifiers should be created by TokenValue(IdentifierInfo *)"); - assert(!tok::isLiteral(Kind) && "Literals are not supported."); - assert(!tok::isAnnotation(Kind) && "Annotations are not supported."); - } - TokenValue(IdentifierInfo *II) : Kind(tok::identifier), II(II) {} - bool operator==(const Token &Tok) const { - return Tok.getKind() == Kind && - (!II || II == Tok.getIdentifierInfo()); - } -}; - -/// \brief Compares macro tokens with a specified token value sequence. -static bool MacroDefinitionEquals(const MacroInfo *MI, - llvm::ArrayRef<TokenValue> Tokens) { - return Tokens.size() == MI->getNumTokens() && - std::equal(Tokens.begin(), Tokens.end(), MI->tokens_begin()); -} - -static std::string GetSuitableSpelling(Preprocessor &PP, SourceLocation L, - llvm::ArrayRef<TokenValue> Tokens, - const char *Spelling) { - SourceManager &SM = PP.getSourceManager(); - SourceLocation BestLocation; - std::string BestSpelling = Spelling; - for (Preprocessor::macro_iterator I = PP.macro_begin(), E = PP.macro_end(); - I != E; ++I) { - if (!I->second->isObjectLike()) - continue; - const MacroInfo *MI = I->second->findDefinitionAtLoc(L, SM); - if (!MI) - continue; - if (!MacroDefinitionEquals(MI, Tokens)) - continue; - SourceLocation Location = I->second->getDefinitionLoc(); - // Choose the macro defined latest. - if (BestLocation.isInvalid() || - (Location.isValid() && - SM.isBeforeInTranslationUnit(BestLocation, Location))) { - BestLocation = Location; - BestSpelling = I->first->getName(); - } - } - return BestSpelling; -} - namespace { class FallthroughMapper : public RecursiveASTVisitor<FallthroughMapper> { public: @@ -914,11 +860,15 @@ static void DiagnoseSwitchLabelsFallthrough(Sema &S, AnalysisDeclContext &AC, tok::coloncolon, PP.getIdentifierInfo("fallthrough"), tok::r_square, tok::r_square }; - std::string AnnotationSpelling = GetSuitableSpelling( - PP, L, Tokens, "[[clang::fallthrough]]"); + StringRef AnnotationSpelling = "[[clang::fallthrough]]"; + StringRef MacroName = PP.getLastMacroWithSpelling(L, Tokens); + if (!MacroName.empty()) + AnnotationSpelling = MacroName; + SmallString<64> TextToInsert(AnnotationSpelling); + TextToInsert += "; "; S.Diag(L, diag::note_insert_fallthrough_fixit) << AnnotationSpelling << - FixItHint::CreateInsertion(L, AnnotationSpelling + "; "); + FixItHint::CreateInsertion(L, TextToInsert); } } S.Diag(L, diag::note_insert_break_fixit) << diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 592956b9db..019a7f141a 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -7266,7 +7266,7 @@ void Sema::ActOnDocumentableDecls(Decl **Group, unsigned NumDecls) { // the lookahead in the lexer: we've consumed the semicolon and looked // ahead through comments. for (unsigned i = 0; i != NumDecls; ++i) - Context.getCommentForDecl(Group[i]); + Context.getCommentForDecl(Group[i], &PP); } } diff --git a/test/Sema/warn-documentation-fixits.cpp b/test/Sema/warn-documentation-fixits.cpp index 812d404e68..a47b776375 100644 --- a/test/Sema/warn-documentation-fixits.cpp +++ b/test/Sema/warn-documentation-fixits.cpp @@ -51,6 +51,12 @@ struct test_deprecated_6 { } }; +#define MY_ATTR_DEPRECATED __attribute__((deprecated)) + +// expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} +/// \deprecated +void test_deprecated_9(int a); + // CHECK: fix-it:"{{.*}}":{5:12-5:22}:"a" // CHECK: fix-it:"{{.*}}":{9:12-9:15}:"aaa" // CHECK: fix-it:"{{.*}}":{13:13-13:23}:"T" @@ -61,4 +67,5 @@ struct test_deprecated_6 { // CHECK: fix-it:"{{.*}}":{38:27-38:27}:" __attribute__((deprecated))" // CHECK: fix-it:"{{.*}}":{46:27-46:27}:" __attribute__((deprecated))" // CHECK: fix-it:"{{.*}}":{50:27-50:27}:" __attribute__((deprecated))" +// CHECK: fix-it:"{{.*}}":{58:30-58:30}:" MY_ATTR_DEPRECATED" diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index f1080f68e8..5ad025a013 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -5810,7 +5810,7 @@ CXComment clang_Cursor_getParsedComment(CXCursor C) { const Decl *D = getCursorDecl(C); const ASTContext &Context = getCursorContext(C); - const comments::FullComment *FC = Context.getCommentForDecl(D); + const comments::FullComment *FC = Context.getCommentForDecl(D, /*PP=*/ NULL); return cxcomment::createCXComment(FC, getCursorTU(C)); } diff --git a/unittests/AST/CommentParser.cpp b/unittests/AST/CommentParser.cpp index 0ed8771e98..d6b1f58f34 100644 --- a/unittests/AST/CommentParser.cpp +++ b/unittests/AST/CommentParser.cpp @@ -59,7 +59,7 @@ FullComment *CommentParserTest::parseString(const char *Source) { Lexer L(Allocator, Traits, Begin, Source, Source + strlen(Source)); - Sema S(Allocator, SourceMgr, Diags, Traits); + Sema S(Allocator, SourceMgr, Diags, Traits, /*PP=*/ NULL); Parser P(L, S, Allocator, SourceMgr, Diags, Traits); FullComment *FC = P.parseFullComment(); |