/// 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;
namespace clang {
class Decl;
class SourceMgr;
+class Preprocessor;
namespace comments {
class CommandTraits;
CommandTraits &Traits;
+ const Preprocessor *PP;
+
/// Information about the declaration this comment is attached to.
DeclInfo *ThisDeclInfo;
public:
Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
- DiagnosticsEngine &Diags, CommandTraits &Traits);
+ DiagnosticsEngine &Diags, CommandTraits &Traits,
+ const Preprocessor *PP);
void setDecl(const Decl *D);
class ASTContext;
class ASTReader;
class Decl;
+class Preprocessor;
namespace comments {
class FullComment;
}
/// 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;
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
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.
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 =
// 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;
}
#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 {
} // 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) {
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);
}
}
}
comments::FullComment *RawComment::parse(const ASTContext &Context,
+ const Preprocessor *PP,
const Decl *D) const {
// Make sure that RawText is valid.
getRawText(Context.getSourceManager());
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(),
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;
#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"
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:
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) <<
// 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);
}
}
}
};
+#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"
// 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"
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));
}
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();