Attaching comments to declarations: find comment attached to any redeclaration

Not only look for the comment near the declaration itself, but also walk the
redeclaration chain: the previous declaration might have had a documentation
comment.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161722 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Dmitri Gribenko 2012-08-11 00:51:43 +00:00
Родитель f9c29088a8
Коммит f50555eede
7 изменённых файлов: 191 добавлений и 65 удалений

Просмотреть файл

@ -433,14 +433,57 @@ public:
/// \brief True if comments are already loaded from ExternalASTSource.
mutable bool CommentsLoaded;
typedef std::pair<const RawComment *, comments::FullComment *>
RawAndParsedComment;
class RawCommentAndCacheFlags {
public:
enum Kind {
/// We searched for a comment attached to the particular declaration, but
/// didn't find any.
///
/// getRaw() == 0.
NoCommentInDecl = 0,
/// \brief Mapping from declarations to their comments.
/// We have found a comment attached to this particular declaration.
///
/// getRaw() != 0.
FromDecl,
/// This declaration does not have an attached comment, and we have
/// searched the redeclaration chain.
///
/// If getRaw() == 0, the whole redeclaration chain does not have any
/// comments.
///
/// If getRaw() != 0, it is a comment propagated from other
/// redeclaration.
FromRedecl
};
Kind getKind() const LLVM_READONLY {
return Data.getInt();
}
void setKind(Kind K) {
Data.setInt(K);
}
const RawComment *getRaw() const LLVM_READONLY {
return Data.getPointer();
}
void setRaw(const RawComment *RC) {
Data.setPointer(RC);
}
private:
llvm::PointerIntPair<const RawComment *, 2, Kind> Data;
};
/// \brief Mapping from declarations to comments attached to any
/// redeclaration.
///
/// Raw comments are owned by Comments list. This mapping is populated
/// lazily.
mutable llvm::DenseMap<const Decl *, RawAndParsedComment> DeclComments;
mutable llvm::DenseMap<const Decl *, RawCommentAndCacheFlags> RedeclComments;
/// \brief Return the documentation comment attached to a given declaration,
/// without looking into cache.
@ -457,7 +500,7 @@ public:
/// \brief Return the documentation comment attached to a given declaration.
/// Returns NULL if no comment is attached.
const RawComment *getRawCommentForDecl(const Decl *D) const;
const RawComment *getRawCommentForAnyRedecl(const Decl *D) const;
/// Return parsed documentation comment attached to a given declaration.
/// Returns NULL if no comment is attached.

Просмотреть файл

@ -17,6 +17,11 @@ namespace clang {
class ASTContext;
class ASTReader;
class Decl;
namespace comments {
class FullComment;
} // end namespace comments
class RawComment {
public:
@ -48,12 +53,18 @@ public:
return Kind == RCK_Merged;
}
/// Is this comment attached to any declaration?
bool isAttached() const LLVM_READONLY {
return IsAttached;
return !DeclOrParsedComment.isNull();
}
void setAttached() {
IsAttached = true;
/// Return the declaration that this comment is attached to.
const Decl *getDecl() const;
/// Set the declaration that this comment is attached to.
void setDecl(const Decl *D) {
assert(DeclOrParsedComment.isNull());
DeclOrParsedComment = D;
}
/// Returns true if it is a comment that should be put after a member:
@ -107,20 +118,28 @@ public:
return extractBriefText(Context);
}
/// Returns a \c FullComment AST node, parsing the comment if needed.
comments::FullComment *getParsed(const ASTContext &Context) const {
if (comments::FullComment *FC =
DeclOrParsedComment.dyn_cast<comments::FullComment *>())
return FC;
return parse(Context);
}
private:
SourceRange Range;
mutable StringRef RawText;
mutable const char *BriefText;
mutable llvm::PointerUnion<const Decl *, comments::FullComment *>
DeclOrParsedComment;
mutable bool RawTextValid : 1; ///< True if RawText is valid
mutable bool BriefTextValid : 1; ///< True if BriefText is valid
unsigned Kind : 3;
/// True if comment is attached to a declaration in ASTContext.
bool IsAttached : 1;
bool IsTrailingComment : 1;
bool IsAlmostTrailingComment : 1;
@ -133,7 +152,7 @@ private:
RawComment(SourceRange SR, CommentKind K, bool IsTrailingComment,
bool IsAlmostTrailingComment) :
Range(SR), RawTextValid(false), BriefTextValid(false), Kind(K),
IsAttached(false), IsTrailingComment(IsTrailingComment),
IsTrailingComment(IsTrailingComment),
IsAlmostTrailingComment(IsAlmostTrailingComment),
BeginLineValid(false), EndLineValid(false)
{ }
@ -142,6 +161,8 @@ private:
const char *extractBriefText(const ASTContext &Context) const;
comments::FullComment *parse(const ASTContext &Context) const;
friend class ASTReader;
};

Просмотреть файл

@ -13,11 +13,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/Comment.h"
#include "clang/AST/CommentCommandTraits.h"
#include "clang/AST/CommentLexer.h"
#include "clang/AST/CommentSema.h"
#include "clang/AST/CommentParser.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
@ -149,6 +145,7 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
SourceMgr.getLineNumber(DeclLocDecomp.first, DeclLocDecomp.second)
== SourceMgr.getLineNumber(CommentBeginDecomp.first,
CommentBeginDecomp.second)) {
(*Comment)->setDecl(D);
return *Comment;
}
}
@ -188,59 +185,74 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
if (Text.find_first_of(",;{}#@") != StringRef::npos)
return NULL;
(*Comment)->setDecl(D);
return *Comment;
}
const RawComment *ASTContext::getRawCommentForDecl(const Decl *D) const {
// Check whether we have cached a comment string for this declaration
// already.
llvm::DenseMap<const Decl *, RawAndParsedComment>::iterator Pos
= DeclComments.find(D);
if (Pos != DeclComments.end()) {
RawAndParsedComment C = Pos->second;
return C.first;
const RawComment *ASTContext::getRawCommentForAnyRedecl(const Decl *D) const {
// Check whether we have cached a comment for this declaration already.
{
llvm::DenseMap<const Decl *, RawCommentAndCacheFlags>::iterator Pos =
RedeclComments.find(D);
if (Pos != RedeclComments.end()) {
const RawCommentAndCacheFlags &Raw = Pos->second;
if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl)
return Raw.getRaw();
}
}
// Search for comments attached to declarations in the redeclaration chain.
const RawComment *RC = NULL;
for (Decl::redecl_iterator I = D->redecls_begin(),
E = D->redecls_end();
I != E; ++I) {
llvm::DenseMap<const Decl *, RawCommentAndCacheFlags>::iterator Pos =
RedeclComments.find(*I);
if (Pos != RedeclComments.end()) {
const RawCommentAndCacheFlags &Raw = Pos->second;
if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) {
RC = Raw.getRaw();
break;
}
} else {
RC = getRawCommentForDeclNoCache(*I);
RawCommentAndCacheFlags Raw;
if (RC) {
Raw.setRaw(RC);
Raw.setKind(RawCommentAndCacheFlags::FromDecl);
} else
Raw.setKind(RawCommentAndCacheFlags::NoCommentInDecl);
RedeclComments[*I] = Raw;
if (RC)
break;
}
}
RawComment *RC = getRawCommentForDeclNoCache(D);
// If we found a comment, it should be a documentation comment.
assert(!RC || RC->isDocumentation());
DeclComments[D] =
RawAndParsedComment(RC, static_cast<comments::FullComment *>(NULL));
if (RC)
RC->setAttached();
// Update cache for every declaration in the redeclaration chain.
RawCommentAndCacheFlags Raw;
Raw.setRaw(RC);
Raw.setKind(RawCommentAndCacheFlags::FromRedecl);
for (Decl::redecl_iterator I = D->redecls_begin(),
E = D->redecls_end();
I != E; ++I) {
RawCommentAndCacheFlags &R = RedeclComments[*I];
if (R.getKind() == RawCommentAndCacheFlags::NoCommentInDecl)
R = Raw;
}
return RC;
}
comments::FullComment *ASTContext::getCommentForDecl(const Decl *D) const {
llvm::DenseMap<const Decl *, RawAndParsedComment>::iterator Pos
= DeclComments.find(D);
const RawComment *RC;
if (Pos != DeclComments.end()) {
RawAndParsedComment C = Pos->second;
if (comments::FullComment *FC = C.second)
return FC;
RC = C.first;
} else
RC = getRawCommentForDecl(D);
const RawComment *RC = getRawCommentForAnyRedecl(D);
if (!RC)
return NULL;
const StringRef RawText = RC->getRawText(SourceMgr);
comments::CommandTraits Traits;
comments::Lexer L(getAllocator(), Traits,
RC->getSourceRange().getBegin(), comments::CommentOptions(),
RawText.begin(), RawText.end());
comments::Sema S(getAllocator(), getSourceManager(), getDiagnostics(),
Traits);
S.setDecl(D);
comments::Parser P(L, S, getAllocator(), getSourceManager(),
getDiagnostics(), Traits);
comments::FullComment *FC = P.parseFullComment();
DeclComments[D].second = FC;
return FC;
return RC->getParsed(*this);
}
void

Просмотреть файл

@ -9,8 +9,11 @@
#include "clang/AST/RawCommentList.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Comment.h"
#include "clang/AST/CommentLexer.h"
#include "clang/AST/CommentBriefParser.h"
#include "clang/AST/CommentSema.h"
#include "clang/AST/CommentParser.h"
#include "clang/AST/CommentCommandTraits.h"
#include "llvm/ADT/STLExtras.h"
@ -62,7 +65,7 @@ bool mergedCommentIsTrailingComment(StringRef Comment) {
RawComment::RawComment(const SourceManager &SourceMgr, SourceRange SR,
bool Merged) :
Range(SR), RawTextValid(false), BriefTextValid(false),
IsAttached(false), IsAlmostTrailingComment(false),
IsAlmostTrailingComment(false),
BeginLineValid(false), EndLineValid(false) {
// Extract raw comment text, if possible.
if (SR.getBegin() == SR.getEnd() || getRawText(SourceMgr).empty()) {
@ -84,6 +87,16 @@ RawComment::RawComment(const SourceManager &SourceMgr, SourceRange SR,
}
}
const Decl *RawComment::getDecl() const {
if (DeclOrParsedComment.isNull())
return NULL;
if (const Decl *D = DeclOrParsedComment.dyn_cast<const Decl *>())
return D;
return DeclOrParsedComment.get<comments::FullComment *>()->getDecl();
}
unsigned RawComment::getBeginLine(const SourceManager &SM) const {
if (BeginLineValid)
return BeginLine;
@ -156,6 +169,25 @@ const char *RawComment::extractBriefText(const ASTContext &Context) const {
return BriefTextPtr;
}
comments::FullComment *RawComment::parse(const ASTContext &Context) const {
// Make sure that RawText is valid.
getRawText(Context.getSourceManager());
comments::CommandTraits Traits;
comments::Lexer L(Context.getAllocator(), Traits,
getSourceRange().getBegin(), comments::CommentOptions(),
RawText.begin(), RawText.end());
comments::Sema S(Context.getAllocator(), Context.getSourceManager(),
Context.getDiagnostics(), Traits);
S.setDecl(getDecl());
comments::Parser P(L, S, Context.getAllocator(), Context.getSourceManager(),
Context.getDiagnostics(), Traits);
comments::FullComment *FC = P.parseFullComment();
DeclOrParsedComment = FC;
return FC;
}
namespace {
bool containsOnlyWhitespace(StringRef Str) {
return Str.find_first_not_of(" \t\f\v\r\n") == StringRef::npos;

Просмотреть файл

@ -2543,7 +2543,7 @@ CodeCompletionResult::CreateCodeCompletionString(ASTContext &Ctx,
if (IncludeBriefComments) {
// Add documentation comment, if it exists.
if (const RawComment *RC = Ctx.getRawCommentForDecl(ND)) {
if (const RawComment *RC = Ctx.getRawCommentForAnyRedecl(ND)) {
Result.addBriefComment(RC->getBriefText(Ctx));
}
}

Просмотреть файл

@ -17,17 +17,35 @@ public:
namespace T5 {
}
void test() {
struct T6 {
/// \brief Fff.
void T7();
T2 t2;
t2.
/// \brief Ggg.
void T8();
};
void T6::T7() {
}
// RUN: env CINDEXTEST_COMPLETION_BRIEF_COMMENTS=1 c-index-test -code-completion-at=%s:21:1 %s | FileCheck -check-prefix=CC1 %s
void test1() {
T2 t2;
t2.T4;
T6 t6;
t6.T8();
}
// RUN: env CINDEXTEST_COMPLETION_BRIEF_COMMENTS=1 c-index-test -code-completion-at=%s:32:1 %s | FileCheck -check-prefix=CC1 %s
// CHECK-CC1: FunctionDecl:{ResultType void}{TypedText T1}{{.*}}(brief comment: Aaa.)
// CHECK-CC1: ClassDecl:{TypedText T2}{{.*}}(brief comment: Bbb.)
// CHECK-CC1: Namespace:{TypedText T5}{{.*}}(brief comment: Eee.)
// RUN: env CINDEXTEST_COMPLETION_BRIEF_COMMENTS=1 c-index-test -code-completion-at=%s:23:6 %s | FileCheck -check-prefix=CC2 %s
// RUN: env CINDEXTEST_COMPLETION_BRIEF_COMMENTS=1 c-index-test -code-completion-at=%s:34:6 %s | FileCheck -check-prefix=CC2 %s
// CHECK-CC2: CXXMethod:{ResultType void}{TypedText T3}{{.*}}(brief comment: Ccc.)
// CHECK-CC2: FieldDecl:{ResultType int}{TypedText T4}{{.*}}(brief comment: Ddd.)
// RUN: env CINDEXTEST_COMPLETION_BRIEF_COMMENTS=1 c-index-test -code-completion-at=%s:37:6 %s | FileCheck -check-prefix=CC3 %s
// CHECK-CC3: CXXMethod:{ResultType void}{TypedText T7}{LeftParen (}{RightParen )} (34) (parent: StructDecl 'T6')(brief comment: Fff.)
// CHECK-CC3: CXXMethod:{ResultType void}{TypedText T8}{LeftParen (}{RightParen )} (34) (parent: StructDecl 'T6')(brief comment: Ggg.)

Просмотреть файл

@ -5689,7 +5689,7 @@ CXSourceRange clang_Cursor_getCommentRange(CXCursor C) {
const Decl *D = getCursorDecl(C);
ASTContext &Context = getCursorContext(C);
const RawComment *RC = Context.getRawCommentForDecl(D);
const RawComment *RC = Context.getRawCommentForAnyRedecl(D);
if (!RC)
return clang_getNullRange();
@ -5702,7 +5702,7 @@ CXString clang_Cursor_getRawCommentText(CXCursor C) {
const Decl *D = getCursorDecl(C);
ASTContext &Context = getCursorContext(C);
const RawComment *RC = Context.getRawCommentForDecl(D);
const RawComment *RC = Context.getRawCommentForAnyRedecl(D);
StringRef RawText = RC ? RC->getRawText(Context.getSourceManager()) :
StringRef();
@ -5717,7 +5717,7 @@ CXString clang_Cursor_getBriefCommentText(CXCursor C) {
const Decl *D = getCursorDecl(C);
const ASTContext &Context = getCursorContext(C);
const RawComment *RC = Context.getRawCommentForDecl(D);
const RawComment *RC = Context.getRawCommentForAnyRedecl(D);
if (RC) {
StringRef BriefText = RC->getBriefText(Context);