Comment parsing: add support for \tparam command on all levels.

The only caveat is renumbering CXCommentKind enum for aesthetic reasons -- this
breaks libclang binary compatibility, but should not be a problem since API is
so new.

This also fixes PR13372 as a side-effect.

llvm-svn: 161087
This commit is contained in:
Dmitri Gribenko 2012-07-31 22:37:06 +00:00
parent 708709c015
commit 34df220410
19 changed files with 1041 additions and 75 deletions

View File

@ -3276,6 +3276,14 @@ enum CXCommentKind {
*/
CXComment_ParamCommand = 7,
/**
* \brief A \\tparam command that describes a template parameter (name and
* description).
*
* \brief For example: \\tparam T description.
*/
CXComment_TParamCommand = 8,
/**
* \brief A verbatim block command (e. g., preformatted code). Verbatim
* block has an opening and a closing command and contains multiple lines of
@ -3286,25 +3294,25 @@ enum CXCommentKind {
* aaa
* \\endverbatim
*/
CXComment_VerbatimBlockCommand = 8,
CXComment_VerbatimBlockCommand = 9,
/**
* \brief A line of text that is contained within a
* CXComment_VerbatimBlockCommand node.
*/
CXComment_VerbatimBlockLine = 9,
CXComment_VerbatimBlockLine = 10,
/**
* \brief A verbatim line command. Verbatim line has an opening command,
* a single line of text (up to the newline after the opening command) and
* has no closing command.
*/
CXComment_VerbatimLine = 10,
CXComment_VerbatimLine = 11,
/**
* \brief A full comment attached to a declaration, contains block content.
*/
CXComment_FullComment = 11
CXComment_FullComment = 12
};
/**
@ -3563,6 +3571,63 @@ CINDEX_LINKAGE
enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
CXComment Comment);
/**
* \param Comment a \c CXComment_TParamCommand AST node.
*
* \returns template parameter name.
*/
CINDEX_LINKAGE
CXString clang_TParamCommandComment_getParamName(CXComment Comment);
/**
* \param Comment a \c CXComment_TParamCommand AST node.
*
* \returns non-zero if the parameter that this AST node represents was found
* in the template parameter list and
* \c clang_TParamCommandComment_getDepth and
* \c clang_TParamCommandComment_getIndex functions will return a meaningful
* value.
*/
CINDEX_LINKAGE
unsigned clang_TParamCommandComment_isParamPositionValid(CXComment Comment);
/**
* \param Comment a \c CXComment_TParamCommand AST node.
*
* \returns zero-based nesting depth of this parameter in the template parameter list.
*
* For example,
* \verbatim
* template<typename C, template<typename T> class TT>
* void test(TT<int> aaa);
* \endverbatim
* for C and TT nesting depth is 0,
* for T nesting depth is 1.
*/
CINDEX_LINKAGE
unsigned clang_TParamCommandComment_getDepth(CXComment Comment);
/**
* \param Comment a \c CXComment_TParamCommand AST node.
*
* \returns zero-based parameter index in the template parameter list at a
* given nesting depth.
*
* For example,
* \verbatim
* template<typename C, template<typename T> class TT>
* void test(TT<int> aaa);
* \endverbatim
* for C and TT nesting depth is 0, so we can ask for index at depth 0:
* at depth 0 C's index is 0, TT's index is 1.
*
* For T nesting depth is 1, so we can ask for index at depth 0 and 1:
* at depth 0 T's index is 1 (same as TT's),
* at depth 1 T's index is 0.
*/
CINDEX_LINKAGE
unsigned clang_TParamCommandComment_getIndex(CXComment Comment, unsigned Depth);
/**
* \param Comment a \c CXComment_VerbatimBlockLine AST node.
*
@ -3606,6 +3671,15 @@ CINDEX_LINKAGE CXString clang_HTMLTagComment_getAsString(CXComment Comment);
* \li "param-name-index-invalid" and "param-descr-index-invalid" are used if
* parameter index is invalid.
*
* Template parameter documentation is rendered as a \<dl\> list with
* parameters sorted in template parameter list order. CSS classes used:
* \li "tparam-name-index-NUMBER" for parameter name (\<dt\>);
* \li "tparam-descr-index-NUMBER" for parameter description (\<dd\>);
* \li "taram-name-index-other" and "tparam-descr-index-other" are used for
* names inside template template parameters;
* \li "tparam-name-index-invalid" and "tparam-descr-index-invalid" are used if
* parameter position is invalid.
*
* \param Comment a \c CXComment_FullComment AST node.
*
* \returns string containing an HTML fragment.

View File

@ -723,6 +723,68 @@ public:
}
};
/// Doxygen \\tparam command, describes a template parameter.
class TParamCommandComment : public BlockCommandComment {
private:
/// If this template parameter name was resolved (found in template parameter
/// list), then this stores a list of position indexes in all template
/// parameter lists.
///
/// For example:
/// \verbatim
/// template<typename C, template<typename T> class TT>
/// void test(TT<int> aaa);
/// \endverbatim
/// For C: Position = { 0 }
/// For TT: Position = { 1 }
/// For T: Position = { 1, 0 }
llvm::ArrayRef<unsigned> Position;
public:
TParamCommandComment(SourceLocation LocBegin,
SourceLocation LocEnd,
StringRef Name) :
BlockCommandComment(TParamCommandCommentKind, LocBegin, LocEnd, Name)
{ }
static bool classof(const Comment *C) {
return C->getCommentKind() == TParamCommandCommentKind;
}
static bool classof(const TParamCommandComment *) { return true; }
bool hasParamName() const {
return getNumArgs() > 0;
}
StringRef getParamName() const {
return Args[0].Text;
}
SourceRange getParamNameRange() const {
return Args[0].Range;
}
bool isPositionValid() const LLVM_READONLY {
return !Position.empty();
}
unsigned getDepth() const {
assert(isPositionValid());
return Position.size();
}
unsigned getIndex(unsigned Depth) const {
assert(isPositionValid());
return Position[Depth];
}
void setPosition(ArrayRef<unsigned> NewPosition) {
Position = NewPosition;
assert(isPositionValid());
}
};
/// A line of text contained in a verbatim block.
class VerbatimBlockLineComment : public Comment {
StringRef Text;

View File

@ -42,17 +42,6 @@ class Parser {
/// Source manager for the comment being parsed.
const SourceManager &SourceMgr;
template<typename T>
ArrayRef<T> copyArray(ArrayRef<T> Source) {
size_t Size = Source.size();
if (Size != 0) {
T *Mem = Allocator.Allocate<T>(Size);
std::uninitialized_copy(Source.begin(), Source.end(), Mem);
return llvm::makeArrayRef(Mem, Size);
} else
return llvm::makeArrayRef(static_cast<T *>(NULL), 0);
}
DiagnosticsEngine &Diags;
DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) {
@ -105,6 +94,11 @@ public:
ParamCommandComment *PC,
TextTokenRetokenizer &Retokenizer);
/// Parse arguments for \\tparam command.
TParamCommandComment *parseTParamCommandArgs(
TParamCommandComment *TPC,
TextTokenRetokenizer &Retokenizer);
BlockCommandComment *parseBlockCommandArgs(
BlockCommandComment *BC,
TextTokenRetokenizer &Retokenizer,

View File

@ -19,12 +19,14 @@
#include "clang/AST/Comment.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Allocator.h"
namespace clang {
class Decl;
class FunctionDecl;
class ParmVarDecl;
class TemplateParameterList;
class SourceMgr;
namespace comments {
@ -56,6 +58,18 @@ class Sema {
/// true.
llvm::SmallVector<ParamCommandComment *, 8> ParamVarDocs;
/// Template parameters that can be referenced by \\tparam if \c ThisDecl is
/// a template.
///
/// Contains a valid value if \c IsThisDeclInspected is true.
const TemplateParameterList *TemplateParameters;
/// Comment AST nodes that correspond to parameter names in
/// \c TemplateParameters.
///
/// Contains a valid value if \c IsThisDeclInspected is true.
llvm::StringMap<TParamCommandComment *> TemplateParameterDocs;
/// True if we extracted all important information from \c ThisDecl into
/// \c Sema members.
unsigned IsThisDeclInspected : 1;
@ -64,6 +78,10 @@ class Sema {
/// Contains a valid value if \c IsThisDeclInspected is true.
unsigned IsFunctionDecl : 1;
/// Is \c ThisDecl a template declaration.
/// Contains a valid value if \c IsThisDeclInspected is true.
unsigned IsTemplateDecl : 1;
DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) {
return Diags.Report(Loc, DiagID);
}
@ -78,6 +96,18 @@ public:
void setDecl(const Decl *D);
/// Returns a copy of array, owned by Sema's allocator.
template<typename T>
ArrayRef<T> copyArray(ArrayRef<T> Source) {
size_t Size = Source.size();
if (Size != 0) {
T *Mem = Allocator.Allocate<T>(Size);
std::uninitialized_copy(Source.begin(), Source.end(), Mem);
return llvm::makeArrayRef(Mem, Size);
} else
return llvm::makeArrayRef(static_cast<T *>(NULL), 0);
}
ParagraphComment *actOnParagraphComment(
ArrayRef<InlineContentComment *> Content);
@ -111,6 +141,19 @@ public:
ParamCommandComment *actOnParamCommandFinish(ParamCommandComment *Command,
ParagraphComment *Paragraph);
TParamCommandComment *actOnTParamCommandStart(SourceLocation LocBegin,
SourceLocation LocEnd,
StringRef Name);
TParamCommandComment *actOnTParamCommandParamNameArg(
TParamCommandComment *Command,
SourceLocation ArgLocBegin,
SourceLocation ArgLocEnd,
StringRef Arg);
TParamCommandComment *actOnTParamCommandFinish(TParamCommandComment *Command,
ParagraphComment *Paragraph);
InlineCommandComment *actOnInlineCommand(SourceLocation CommandLocBegin,
SourceLocation CommandLocEnd,
StringRef CommandName);
@ -165,6 +208,8 @@ public:
void checkBlockCommandEmptyParagraph(BlockCommandComment *Command);
bool isFunctionDecl();
bool isTemplateDecl();
ArrayRef<const ParmVarDecl *> getParamVars();
/// Extract all important semantic information from \c ThisDecl into
@ -180,8 +225,17 @@ public:
unsigned correctTypoInParmVarReference(StringRef Typo,
ArrayRef<const ParmVarDecl *> ParamVars);
bool resolveTParamReference(StringRef Name,
const TemplateParameterList *TemplateParameters,
SmallVectorImpl<unsigned> *Position);
StringRef correctTypoInTParamReference(
StringRef Typo,
const TemplateParameterList *TemplateParameters);
bool isBlockCommand(StringRef Name);
bool isParamCommand(StringRef Name);
bool isTParamCommand(StringRef Name);
unsigned getBlockCommandNumArgs(StringRef Name);
bool isInlineCommand(StringRef Name) const;

View File

@ -17,6 +17,7 @@ def BlockContentComment : Comment<1>;
def ParagraphComment : DComment<BlockContentComment>;
def BlockCommandComment : DComment<BlockContentComment>;
def ParamCommandComment : DComment<BlockCommandComment>;
def TParamCommandComment : DComment<BlockCommandComment>;
def VerbatimBlockComment : DComment<BlockCommandComment>;
def VerbatimLineComment : DComment<BlockCommandComment>;

View File

@ -77,5 +77,26 @@ def warn_doc_param_not_found : Warning<
def note_doc_param_name_suggestion : Note<
"did you mean '%0'?">;
// \tparam command
def warn_doc_tparam_not_attached_to_a_template_decl : Warning<
"'\\tparam' command used in a comment that is not attached to "
"a template declaration">,
InGroup<Documentation>, DefaultIgnore;
def warn_doc_tparam_duplicate : Warning<
"template parameter '%0' is already documented">,
InGroup<Documentation>, DefaultIgnore;
def note_doc_tparam_previous : Note<
"previous documentation">;
def warn_doc_tparam_not_found : Warning<
"template parameter '%0' not found in the template declaration">,
InGroup<Documentation>, DefaultIgnore;
def note_doc_tparam_name_suggestion : Note<
"did you mean '%0'?">;
} // end of documentation issue category
} // end of AST component

View File

@ -72,6 +72,13 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
if (isa<ParmVarDecl>(D))
return NULL;
// TODO: we could look up template parameter documentation in the template
// documentation.
if (isa<TemplateTypeParmDecl>(D) ||
isa<NonTypeTemplateParmDecl>(D) ||
isa<TemplateTemplateParmDecl>(D))
return NULL;
ArrayRef<RawComment *> RawComments = Comments.getComments();
// If there are no comments anywhere, we won't find anything.
@ -86,7 +93,9 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
// so we use the location of the identifier as the "declaration location".
SourceLocation DeclLoc;
if (isa<ObjCMethodDecl>(D) || isa<ObjCContainerDecl>(D) ||
isa<ObjCPropertyDecl>(D))
isa<ObjCPropertyDecl>(D) ||
isa<FunctionTemplateDecl>(D) ||
isa<ClassTemplateDecl>(D) || isa<ClassTemplateSpecializationDecl>(D))
DeclLoc = D->getLocStart();
else
DeclLoc = D->getLocation();

View File

@ -48,6 +48,7 @@ bool isBlockCommand(StringRef Name) {
.Case("pre", true)
.Case("post", true)
.Cases("param", "arg", true)
.Case("tparam", true)
.Default(false);
}
} // unnamed namespace

View File

@ -50,6 +50,7 @@ public:
void visitParagraphComment(const ParagraphComment *C);
void visitBlockCommandComment(const BlockCommandComment *C);
void visitParamCommandComment(const ParamCommandComment *C);
void visitTParamCommandComment(const TParamCommandComment *C);
void visitVerbatimBlockComment(const VerbatimBlockComment *C);
void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
void visitVerbatimLineComment(const VerbatimLineComment *C);
@ -176,6 +177,24 @@ void CommentDumper::visitParamCommandComment(const ParamCommandComment *C) {
OS << " ParamIndex=" << C->getParamIndex();
}
void CommentDumper::visitTParamCommandComment(const TParamCommandComment *C) {
dumpComment(C);
if (C->hasParamName()) {
OS << " Param=\"" << C->getParamName() << "\"";
}
if (C->isPositionValid()) {
OS << " Position=<";
for (unsigned i = 0, e = C->getDepth(); i != e; ++i) {
OS << C->getIndex(i);
if (i != e - 1)
OS << ", ";
}
OS << ">";
}
}
void CommentDumper::visitVerbatimBlockComment(const VerbatimBlockComment *C) {
dumpComment(C);

View File

@ -276,6 +276,19 @@ ParamCommandComment *Parser::parseParamCommandArgs(
return PC;
}
TParamCommandComment *Parser::parseTParamCommandArgs(
TParamCommandComment *TPC,
TextTokenRetokenizer &Retokenizer) {
Token Arg;
if (Retokenizer.lexWord(Arg))
TPC = S.actOnTParamCommandParamNameArg(TPC,
Arg.getLocation(),
Arg.getEndLocation(),
Arg.getText());
return TPC;
}
BlockCommandComment *Parser::parseBlockCommandArgs(
BlockCommandComment *BC,
TextTokenRetokenizer &Retokenizer,
@ -299,14 +312,21 @@ BlockCommandComment *Parser::parseBlockCommand() {
assert(Tok.is(tok::command));
ParamCommandComment *PC;
TParamCommandComment *TPC;
BlockCommandComment *BC;
bool IsParam = false;
bool IsTParam = false;
unsigned NumArgs = 0;
if (S.isParamCommand(Tok.getCommandName())) {
IsParam = true;
PC = S.actOnParamCommandStart(Tok.getLocation(),
Tok.getEndLocation(),
Tok.getCommandName());
} if (S.isTParamCommand(Tok.getCommandName())) {
IsTParam = true;
TPC = S.actOnTParamCommandStart(Tok.getLocation(),
Tok.getEndLocation(),
Tok.getCommandName());
} else {
NumArgs = S.getBlockCommandNumArgs(Tok.getCommandName());
BC = S.actOnBlockCommandStart(Tok.getLocation(),
@ -323,13 +343,15 @@ BlockCommandComment *Parser::parseBlockCommand() {
return S.actOnBlockCommandFinish(IsParam ? PC : BC, Paragraph);
}
if (IsParam || NumArgs > 0) {
if (IsParam || IsTParam || NumArgs > 0) {
// In order to parse command arguments we need to retokenize a few
// following text tokens.
TextTokenRetokenizer Retokenizer(Allocator, *this);
if (IsParam)
PC = parseParamCommandArgs(PC, Retokenizer);
else if (IsTParam)
TPC = parseTParamCommandArgs(TPC, Retokenizer);
else
BC = parseBlockCommandArgs(BC, Retokenizer, NumArgs);
@ -341,6 +363,8 @@ BlockCommandComment *Parser::parseBlockCommand() {
// paragraph.
if (IsParam)
return S.actOnParamCommandFinish(PC, cast<ParagraphComment>(Block));
else if (IsTParam)
return S.actOnTParamCommandFinish(TPC, cast<ParagraphComment>(Block));
else
return S.actOnBlockCommandFinish(BC, cast<ParagraphComment>(Block));
}
@ -419,7 +443,7 @@ HTMLStartTagComment *Parser::parseHTMLStartTag() {
case tok::html_greater:
HST = S.actOnHTMLStartTagFinish(HST,
copyArray(llvm::makeArrayRef(Attrs)),
S.copyArray(llvm::makeArrayRef(Attrs)),
Tok.getLocation(),
/* IsSelfClosing = */ false);
consumeToken();
@ -427,7 +451,7 @@ HTMLStartTagComment *Parser::parseHTMLStartTag() {
case tok::html_slash_greater:
HST = S.actOnHTMLStartTagFinish(HST,
copyArray(llvm::makeArrayRef(Attrs)),
S.copyArray(llvm::makeArrayRef(Attrs)),
Tok.getLocation(),
/* IsSelfClosing = */ true);
consumeToken();
@ -446,14 +470,14 @@ HTMLStartTagComment *Parser::parseHTMLStartTag() {
continue;
return S.actOnHTMLStartTagFinish(HST,
copyArray(llvm::makeArrayRef(Attrs)),
S.copyArray(llvm::makeArrayRef(Attrs)),
SourceLocation(),
/* IsSelfClosing = */ false);
default:
// Not a token from an HTML start tag. Thus HTML tag prematurely ended.
HST = S.actOnHTMLStartTagFinish(HST,
copyArray(llvm::makeArrayRef(Attrs)),
S.copyArray(llvm::makeArrayRef(Attrs)),
SourceLocation(),
/* IsSelfClosing = */ false);
bool StartLineInvalid;
@ -563,7 +587,7 @@ BlockContentComment *Parser::parseParagraphOrBlockCommand() {
break;
}
return S.actOnParagraphComment(copyArray(llvm::makeArrayRef(Content)));
return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
}
VerbatimBlockComment *Parser::parseVerbatimBlock() {
@ -601,12 +625,12 @@ VerbatimBlockComment *Parser::parseVerbatimBlock() {
if (Tok.is(tok::verbatim_block_end)) {
VB = S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
Tok.getVerbatimBlockName(),
copyArray(llvm::makeArrayRef(Lines)));
S.copyArray(llvm::makeArrayRef(Lines)));
consumeToken();
} else {
// Unterminated \\verbatim block
VB = S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
copyArray(llvm::makeArrayRef(Lines)));
S.copyArray(llvm::makeArrayRef(Lines)));
}
return VB;
@ -680,7 +704,7 @@ FullComment *Parser::parseFullComment() {
while (Tok.is(tok::newline))
consumeToken();
}
return S.actOnFullComment(copyArray(llvm::makeArrayRef(Blocks)));
return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
}
} // end namespace comments

View File

@ -11,6 +11,7 @@
#include "clang/AST/CommentDiagnostic.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/StringSwitch.h"
@ -200,6 +201,90 @@ ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
return Command;
}
TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
SourceLocation LocEnd,
StringRef Name) {
TParamCommandComment *Command =
new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
if (!isTemplateDecl())
Diag(Command->getLocation(),
diag::warn_doc_tparam_not_attached_to_a_template_decl)
<< Command->getCommandNameRange();
return Command;
}
TParamCommandComment *Sema::actOnTParamCommandParamNameArg(
TParamCommandComment *Command,
SourceLocation ArgLocBegin,
SourceLocation ArgLocEnd,
StringRef Arg) {
// Parser will not feed us more arguments than needed.
assert(Command->getNumArgs() == 0);
typedef BlockCommandComment::Argument Argument;
Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
ArgLocEnd),
Arg);
Command->setArgs(llvm::makeArrayRef(A, 1));
if (!isTemplateDecl()) {
// We already warned that this \\tparam is not attached to a template decl.
return Command;
}
SmallVector<unsigned, 2> Position;
if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
TemplateParameterDocs.find(Arg);
if (PrevCommandIt != TemplateParameterDocs.end()) {
SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
<< Arg << ArgRange;
TParamCommandComment *PrevCommand = PrevCommandIt->second;
Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
<< PrevCommand->getParamNameRange();
}
TemplateParameterDocs[Arg] = Command;
return Command;
}
SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
<< Arg << ArgRange;
if (!TemplateParameters || TemplateParameters->size() == 0)
return Command;
StringRef CorrectedName;
if (TemplateParameters->size() == 1) {
const NamedDecl *Param = TemplateParameters->getParam(0);
const IdentifierInfo *II = Param->getIdentifier();
if (II)
CorrectedName = II->getName();
} else {
CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
}
if (!CorrectedName.empty()) {
Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
<< CorrectedName
<< FixItHint::CreateReplacement(ArgRange, CorrectedName);
}
return Command;
}
TParamCommandComment *Sema::actOnTParamCommandFinish(
TParamCommandComment *Command,
ParagraphComment *Paragraph) {
Command->setParagraph(Paragraph);
checkBlockCommandEmptyParagraph(Command);
return Command;
}
InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
SourceLocation CommandLocEnd,
StringRef CommandName) {
@ -387,6 +472,12 @@ bool Sema::isFunctionDecl() {
return IsFunctionDecl;
}
bool Sema::isTemplateDecl() {
if (!IsThisDeclInspected)
inspectThisDecl();
return IsTemplateDecl;
}
ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
if (!IsThisDeclInspected)
inspectThisDecl();
@ -397,18 +488,56 @@ void Sema::inspectThisDecl() {
assert(!IsThisDeclInspected);
if (!ThisDecl) {
IsFunctionDecl = false;
IsTemplateDecl = false;
ParamVars = ArrayRef<const ParmVarDecl *>();
TemplateParameters = NULL;
} else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ThisDecl)) {
IsFunctionDecl = true;
IsTemplateDecl = false;
ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(),
FD->getNumParams());
TemplateParameters = NULL;
unsigned NumLists = FD->getNumTemplateParameterLists();
if (NumLists != 0) {
IsTemplateDecl = true;
TemplateParameters = FD->getTemplateParameterList(NumLists - 1);
}
} else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ThisDecl)) {
IsFunctionDecl = true;
IsTemplateDecl = false;
ParamVars = ArrayRef<const ParmVarDecl *>(MD->param_begin(),
MD->param_size());
TemplateParameters = NULL;
} else if (const FunctionTemplateDecl *FTD =
dyn_cast<FunctionTemplateDecl>(ThisDecl)) {
IsFunctionDecl = true;
IsTemplateDecl = true;
const FunctionDecl *FD = FTD->getTemplatedDecl();
ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(),
FD->getNumParams());
TemplateParameters = FTD->getTemplateParameters();
} else if (const ClassTemplateDecl *CTD =
dyn_cast<ClassTemplateDecl>(ThisDecl)) {
IsFunctionDecl = false;
IsTemplateDecl = true;
ParamVars = ArrayRef<const ParmVarDecl *>();
TemplateParameters = CTD->getTemplateParameters();
} else if (const ClassTemplatePartialSpecializationDecl *CTPSD =
dyn_cast<ClassTemplatePartialSpecializationDecl>(ThisDecl)) {
IsFunctionDecl = false;
IsTemplateDecl = true;
ParamVars = ArrayRef<const ParmVarDecl *>();
TemplateParameters = CTPSD->getTemplateParameters();
} else if (isa<ClassTemplateSpecializationDecl>(ThisDecl)) {
IsFunctionDecl = false;
IsTemplateDecl = true;
ParamVars = ArrayRef<const ParmVarDecl *>();
TemplateParameters = NULL;
} else {
IsFunctionDecl = false;
IsTemplateDecl = false;
ParamVars = ArrayRef<const ParmVarDecl *>();
TemplateParameters = NULL;
}
ParamVarDocs.resize(ParamVars.size(), NULL);
IsThisDeclInspected = true;
@ -424,34 +553,136 @@ unsigned Sema::resolveParmVarReference(StringRef Name,
return ParamCommandComment::InvalidParamIndex;
}
namespace {
class SimpleTypoCorrector {
StringRef Typo;
const unsigned MaxEditDistance;
const NamedDecl *BestDecl;
unsigned BestEditDistance;
unsigned BestIndex;
unsigned NextIndex;
public:
SimpleTypoCorrector(StringRef Typo) :
Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
BestIndex(0), NextIndex(0)
{ }
void addDecl(const NamedDecl *ND);
const NamedDecl *getBestDecl() const {
if (BestEditDistance > MaxEditDistance)
return NULL;
return BestDecl;
}
unsigned getBestDeclIndex() const {
assert(getBestDecl());
return BestIndex;
}
};
void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
unsigned CurrIndex = NextIndex++;
const IdentifierInfo *II = ND->getIdentifier();
if (!II)
return;
StringRef Name = II->getName();
unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
if (MinPossibleEditDistance > 0 &&
Typo.size() / MinPossibleEditDistance < 3)
return;
unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
if (EditDistance < BestEditDistance) {
BestEditDistance = EditDistance;
BestDecl = ND;
BestIndex = CurrIndex;
}
}
} // unnamed namespace
unsigned Sema::correctTypoInParmVarReference(
StringRef Typo,
ArrayRef<const ParmVarDecl *> ParamVars) {
const unsigned MaxEditDistance = (Typo.size() + 2) / 3;
unsigned BestPVDIndex = 0;
unsigned BestEditDistance = MaxEditDistance + 1;
for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
const IdentifierInfo *II = ParamVars[i]->getIdentifier();
if (II) {
StringRef Name = II->getName();
unsigned MinPossibleEditDistance =
abs((int)Name.size() - (int)Typo.size());
if (MinPossibleEditDistance > 0 &&
Typo.size() / MinPossibleEditDistance < 3)
continue;
SimpleTypoCorrector Corrector(Typo);
for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
Corrector.addDecl(ParamVars[i]);
if (Corrector.getBestDecl())
return Corrector.getBestDeclIndex();
else
return ParamCommandComment::InvalidParamIndex;;
}
unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
if (EditDistance < BestEditDistance) {
BestEditDistance = EditDistance;
BestPVDIndex = i;
}
namespace {
bool ResolveTParamReferenceHelper(
StringRef Name,
const TemplateParameterList *TemplateParameters,
SmallVectorImpl<unsigned> *Position) {
for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
const NamedDecl *Param = TemplateParameters->getParam(i);
const IdentifierInfo *II = Param->getIdentifier();
if (II && II->getName() == Name) {
Position->push_back(i);
return true;
}
if (const TemplateTemplateParmDecl *TTP =
dyn_cast<TemplateTemplateParmDecl>(Param)) {
Position->push_back(i);
if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
Position))
return true;
Position->pop_back();
}
}
return false;
}
} // unnamed namespace
if (BestEditDistance <= MaxEditDistance)
return BestPVDIndex;
else
return ParamCommandComment::InvalidParamIndex;
bool Sema::resolveTParamReference(
StringRef Name,
const TemplateParameterList *TemplateParameters,
SmallVectorImpl<unsigned> *Position) {
Position->clear();
if (!TemplateParameters)
return false;
return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
}
namespace {
void CorrectTypoInTParamReferenceHelper(
const TemplateParameterList *TemplateParameters,
SimpleTypoCorrector &Corrector) {
for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
const NamedDecl *Param = TemplateParameters->getParam(i);
Corrector.addDecl(Param);
if (const TemplateTemplateParmDecl *TTP =
dyn_cast<TemplateTemplateParmDecl>(Param))
CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
Corrector);
}
}
} // unnamed namespace
StringRef Sema::correctTypoInTParamReference(
StringRef Typo,
const TemplateParameterList *TemplateParameters) {
SimpleTypoCorrector Corrector(Typo);
CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
if (const NamedDecl *ND = Corrector.getBestDecl()) {
const IdentifierInfo *II = ND->getIdentifier();
assert(II && "SimpleTypoCorrector should not return this decl");
return II->getName();
}
return StringRef();
}
// TODO: tablegen
@ -465,7 +696,7 @@ bool Sema::isBlockCommand(StringRef Name) {
.Case("authors", true)
.Case("pre", true)
.Case("post", true)
.Default(false) || isParamCommand(Name);
.Default(false) || isParamCommand(Name) || isTParamCommand(Name);
}
bool Sema::isParamCommand(StringRef Name) {
@ -475,6 +706,10 @@ bool Sema::isParamCommand(StringRef Name) {
.Default(false);
}
bool Sema::isTParamCommand(StringRef Name) {
return Name == "tparam";
}
unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
return llvm::StringSwitch<unsigned>(Name)
.Cases("brief", "short", 0)

View File

@ -7618,6 +7618,7 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D) {
<< FD->getName() << "dllimport";
}
}
ActOnDocumentableDecl(FD);
return FD;
}

View File

@ -1139,6 +1139,8 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
if (PrevClassTemplate)
mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl());
ActOnDocumentableDecl(NewTemplate);
return NewTemplate;
}
@ -5568,7 +5570,9 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec,
Decl *Sema::ActOnTemplateDeclarator(Scope *S,
MultiTemplateParamsArg TemplateParameterLists,
Declarator &D) {
return HandleDeclarator(S, D, move(TemplateParameterLists));
Decl *NewDecl = HandleDeclarator(S, D, move(TemplateParameterLists));
ActOnDocumentableDecl(NewDecl);
return NewDecl;
}
Decl *Sema::ActOnStartOfFunctionTemplateDef(Scope *FnBodyScope,

View File

@ -290,6 +290,30 @@ void comment_to_html_conversion_15(int x1, int x2);
/// \param x1 Aaa.
void comment_to_html_conversion_16(int x1, int x2);
/// \tparam
/// \param aaa Blah blah
template<typename T>
void comment_to_html_conversion_17(T aaa);
/// \tparam T2 Bbb
/// \tparam T1 Aaa
template<typename T1, typename T2>
void comment_to_html_conversion_18(T1 aaa, T2 bbb);
/// \tparam T2 Bbb
/// \tparam U Zzz
/// \tparam V Ccc
/// \tparam T1 Aaa
template<typename T1, typename T2, int V>
void comment_to_html_conversion_19(T1 aaa, T2 bbb);
/// \tparam TTT Ddd
/// \tparam C Ccc
/// \tparam T Aaa
/// \tparam TT Bbb
template<template<template<typename T> class TT, class C> class TTT>
void comment_to_html_conversion_20();
/// \brief Aaa.
///
/// Bbb.
@ -297,35 +321,35 @@ void comment_to_html_conversion_16(int x1, int x2);
/// \param x2 Ddd.
/// \param x1 Ccc.
/// \returns Eee.
void comment_to_html_conversion_17(int x1, int x2);
void comment_to_html_conversion_21(int x1, int x2);
/// <br><a href="http://example.com/">Aaa</a>
void comment_to_html_conversion_18();
void comment_to_html_conversion_22();
/// \verbatim
/// <a href="http://example.com/">Aaa</a>
/// <a href='http://example.com/'>Aaa</a>
/// \endverbatim
void comment_to_html_conversion_19();
/// \b Aaa
void comment_to_html_conversion_20();
/// \c Aaa \p Bbb
void comment_to_html_conversion_21();
/// \a Aaa \e Bbb \em Ccc
void comment_to_html_conversion_22();
/// \\ \@ \& \$ \# \< \> \% \" \. \::
void comment_to_html_conversion_23();
/// &amp; &lt; &gt; &quot;
/// \b Aaa
void comment_to_html_conversion_24();
/// <em>0&lt;i</em>
/// \c Aaa \p Bbb
void comment_to_html_conversion_25();
/// \a Aaa \e Bbb \em Ccc
void comment_to_html_conversion_26();
/// \\ \@ \& \$ \# \< \> \% \" \. \::
void comment_to_html_conversion_27();
/// &amp; &lt; &gt; &quot;
void comment_to_html_conversion_28();
/// <em>0&lt;i</em>
void comment_to_html_conversion_29();
#endif
// RUN: rm -rf %t
@ -555,7 +579,70 @@ void comment_to_html_conversion_25();
// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x1] ParamIndex=0
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))]
// CHECK: annotate-comments.cpp:300:6: FunctionDecl=comment_to_html_conversion_17:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa.</p><p> Bbb.</p><dl><dt class="param-name-index-0">x1</dt><dd class="param-descr-index-0"> Ccc. </dd><dt class="param-name-index-1">x2</dt><dd class="param-descr-index-1"> Ddd. </dd></dl><p class="para-returns"><span class="word-returns">Returns</span> Eee.</p>]
// CHECK: annotate-comments.cpp:296:6: FunctionTemplate=comment_to_html_conversion_17:{{.*}} FullCommentAsHTML=[<dl><dt class="param-name-index-0">aaa</dt><dd class="param-descr-index-0"> Blah blah</dd></dl>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
// CHECK-NEXT: (CXComment_TParamCommand ParamName=[] ParamPosition=Invalid
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[aaa] ParamIndex=0
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Blah blah]))))]
// CHECK: annotate-comments.cpp:301:6: FunctionTemplate=comment_to_html_conversion_18:{{.*}} FullCommentAsHTML=[<dl><dt class="taram-name-index-0">T1</dt><dd class="tparam-descr-index-0"> Aaa</dd><dt class="taram-name-index-1">T2</dt><dd class="tparam-descr-index-1"> Bbb </dd></dl>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
// CHECK-NEXT: (CXComment_TParamCommand ParamName=[T2] ParamPosition={1}
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Bbb] HasTrailingNewline)
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
// CHECK-NEXT: (CXComment_TParamCommand ParamName=[T1] ParamPosition={0}
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Aaa]))))]
// CHECK: annotate-comments.cpp:308:6: FunctionTemplate=comment_to_html_conversion_19:{{.*}} FullCommentAsHTML=[<dl><dt class="taram-name-index-0">T1</dt><dd class="tparam-descr-index-0"> Aaa</dd><dt class="taram-name-index-1">T2</dt><dd class="tparam-descr-index-1"> Bbb </dd><dt class="taram-name-index-2">V</dt><dd class="tparam-descr-index-2"> Ccc </dd><dt class="tparam-name-index-invalid">U</dt><dd class="tparam-descr-index-invalid"> Zzz </dd></dl>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
// CHECK-NEXT: (CXComment_TParamCommand ParamName=[T2] ParamPosition={1}
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Bbb] HasTrailingNewline)
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
// CHECK-NEXT: (CXComment_TParamCommand ParamName=[U] ParamPosition=Invalid
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Zzz] HasTrailingNewline)
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
// CHECK-NEXT: (CXComment_TParamCommand ParamName=[V] ParamPosition={2}
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Ccc] HasTrailingNewline)
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
// CHECK-NEXT: (CXComment_TParamCommand ParamName=[T1] ParamPosition={0}
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Aaa]))))]
// CHECK: annotate-comments.cpp:315:6: FunctionTemplate=comment_to_html_conversion_20:{{.*}} FullCommentAsHTML=[<dl><dt class="taram-name-index-0">TTT</dt><dd class="tparam-descr-index-0"> Ddd </dd><dt class="taram-name-index-other">C</dt><dd class="tparam-descr-index-other"> Ccc </dd><dt class="taram-name-index-other">T</dt><dd class="tparam-descr-index-other"> Aaa </dd><dt class="taram-name-index-other">TT</dt><dd class="tparam-descr-index-other"> Bbb</dd></dl>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
// CHECK-NEXT: (CXComment_TParamCommand ParamName=[TTT] ParamPosition={0}
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Ddd] HasTrailingNewline)
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
// CHECK-NEXT: (CXComment_TParamCommand ParamName=[C] ParamPosition={0, 1}
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Ccc] HasTrailingNewline)
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
// CHECK-NEXT: (CXComment_TParamCommand ParamName=[T] ParamPosition={0, 0, 0}
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Aaa] HasTrailingNewline)
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
// CHECK-NEXT: (CXComment_TParamCommand ParamName=[TT] ParamPosition={0, 0}
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Bbb]))))]
// CHECK: annotate-comments.cpp:324:6: FunctionDecl=comment_to_html_conversion_21:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa.</p><p> Bbb.</p><dl><dt class="param-name-index-0">x1</dt><dd class="param-descr-index-0"> Ccc. </dd><dt class="param-name-index-1">x2</dt><dd class="param-descr-index-1"> Ddd. </dd></dl><p class="para-returns"><span class="word-returns">Returns</span> Eee.</p>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
@ -578,7 +665,7 @@ void comment_to_html_conversion_25();
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[returns]
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ Eee.]))))]
// CHECK: annotate-comments.cpp:303:6: FunctionDecl=comment_to_html_conversion_18:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <br><a href="http://example.com/">Aaa</a></p>]
// CHECK: annotate-comments.cpp:327:6: FunctionDecl=comment_to_html_conversion_22:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <br><a href="http://example.com/">Aaa</a></p>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph
@ -587,7 +674,7 @@ void comment_to_html_conversion_25();
// CHECK-NEXT: (CXComment_HTMLStartTag Name=[a] Attrs: href=http://example.com/)
// CHECK-NEXT: (CXComment_Text Text=[Aaa])
// CHECK-NEXT: (CXComment_HTMLEndTag Name=[a])))]
// CHECK: annotate-comments.cpp:309:6: FunctionDecl=comment_to_html_conversion_19:{{.*}} FullCommentAsHTML=[<pre> &lt;a href=&quot;http:&#47;&#47;example.com&#47;&quot;&gt;Aaa&lt;&#47;a&gt;\n &lt;a href=&#39;http:&#47;&#47;example.com&#47;&#39;&gt;Aaa&lt;&#47;a&gt;</pre>]
// CHECK: annotate-comments.cpp:333:6: FunctionDecl=comment_to_html_conversion_23:{{.*}} FullCommentAsHTML=[<pre> &lt;a href=&quot;http:&#47;&#47;example.com&#47;&quot;&gt;Aaa&lt;&#47;a&gt;\n &lt;a href=&#39;http:&#47;&#47;example.com&#47;&#39;&gt;Aaa&lt;&#47;a&gt;</pre>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
@ -595,13 +682,13 @@ void comment_to_html_conversion_25();
// CHECK-NEXT: (CXComment_VerbatimBlockCommand CommandName=[verbatim]
// CHECK-NEXT: (CXComment_VerbatimBlockLine Text=[ <a href="http://example.com/">Aaa</a>])
// CHECK-NEXT: (CXComment_VerbatimBlockLine Text=[ <a href='http://example.com/'>Aaa</a>])))]
// CHECK: annotate-comments.cpp:312:6: FunctionDecl=comment_to_html_conversion_20:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <b>Aaa</b></p>]
// CHECK: annotate-comments.cpp:336:6: FunctionDecl=comment_to_html_conversion_24:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <b>Aaa</b></p>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
// CHECK-NEXT: (CXComment_InlineCommand CommandName=[b] RenderBold Arg[0]=Aaa)))]
// CHECK: annotate-comments.cpp:315:6: FunctionDecl=comment_to_html_conversion_21:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <tt>Aaa</tt> <tt>Bbb</tt></p>]
// CHECK: annotate-comments.cpp:339:6: FunctionDecl=comment_to_html_conversion_25:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <tt>Aaa</tt> <tt>Bbb</tt></p>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph
@ -609,7 +696,7 @@ void comment_to_html_conversion_25();
// CHECK-NEXT: (CXComment_InlineCommand CommandName=[c] RenderMonospaced Arg[0]=Aaa)
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
// CHECK-NEXT: (CXComment_InlineCommand CommandName=[p] RenderMonospaced Arg[0]=Bbb)))]
// CHECK: annotate-comments.cpp:318:6: FunctionDecl=comment_to_html_conversion_22:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>Aaa</em> <em>Bbb</em> <em>Ccc</em></p>]
// CHECK: annotate-comments.cpp:342:6: FunctionDecl=comment_to_html_conversion_26:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>Aaa</em> <em>Bbb</em> <em>Ccc</em></p>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph
@ -619,7 +706,7 @@ void comment_to_html_conversion_25();
// CHECK-NEXT: (CXComment_InlineCommand CommandName=[e] RenderEmphasized Arg[0]=Bbb)
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
// CHECK-NEXT: (CXComment_InlineCommand CommandName=[em] RenderEmphasized Arg[0]=Ccc)))]
// CHECK: annotate-comments.cpp:321:6: FunctionDecl=comment_to_html_conversion_23:{{.*}} FullCommentAsHTML=[<p class="para-brief"> \ @ &amp; $ # &lt; &gt; % &quot; . ::</p>]
// CHECK: annotate-comments.cpp:345:6: FunctionDecl=comment_to_html_conversion_27:{{.*}} FullCommentAsHTML=[<p class="para-brief"> \ @ &amp; $ # &lt; &gt; % &quot; . ::</p>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph
@ -645,7 +732,7 @@ void comment_to_html_conversion_25();
// CHECK-NEXT: (CXComment_Text Text=[.])
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
// CHECK-NEXT: (CXComment_Text Text=[::])))]
// CHECK: annotate-comments.cpp:324:6: FunctionDecl=comment_to_html_conversion_24:{{.*}} FullCommentAsHTML=[<p class="para-brief"> &amp; &lt; &gt; &quot;</p>]
// CHECK: annotate-comments.cpp:348:6: FunctionDecl=comment_to_html_conversion_28:{{.*}} FullCommentAsHTML=[<p class="para-brief"> &amp; &lt; &gt; &quot;</p>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph
@ -657,7 +744,7 @@ void comment_to_html_conversion_25();
// CHECK-NEXT: (CXComment_Text Text=[>])
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
// CHECK-NEXT: (CXComment_Text Text=["])))]
// CHECK: annotate-comments.cpp:327:6: FunctionDecl=comment_to_html_conversion_25:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>0&lt;i</em></p>]
// CHECK: annotate-comments.cpp:351:6: FunctionDecl=comment_to_html_conversion_29:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>0&lt;i</em></p>]
// CHECK-NEXT: CommentAST=[
// CHECK-NEXT: (CXComment_FullComment
// CHECK-NEXT: (CXComment_Paragraph

View File

@ -170,18 +170,86 @@ class C {
int test_param15(int bbb, int ccc);
};
// expected-warning@+1 {{parameter 'aab' not found in the function declaration}}
/// \param aab Blah blah.
template<typename T>
void test_param16(int bbb, int ccc);
// expected-warning@+3 {{parameter 'a' is already documented}}
// expected-note@+1 {{previous documentation}}
/// \param a Aaa.
/// \param a Aaa.
int test_param16(int a);
int test_param17(int a);
// expected-warning@+4 {{parameter 'x2' is already documented}}
// expected-note@+2 {{previous documentation}}
/// \param x1 Aaa.
/// \param x2 Bbb.
/// \param x2 Ccc.
int test_param17(int x1, int x2, int x3);
int test_param18(int x1, int x2, int x3);
// expected-warning@+1 {{'\tparam' command used in a comment that is not attached to a template declaration}}
/// \tparam T Aaa
int test_tparam1;
// expected-warning@+1 {{'\tparam' command used in a comment that is not attached to a template declaration}}
/// \tparam T Aaa
void test_tparam2(int aaa);
// expected-warning@+1 {{empty paragraph passed to '\tparam' command}}
/// \tparam
/// \param aaa Blah blah
template<typename T>
void test_tparam3(T aaa);
// expected-warning@+1 {{template parameter 'T' not found in the template declaration}} expected-note@+1 {{did you mean 'TT'?}}
/// \tparam T Aaa
template<typename TT>
void test_tparam4(TT aaa);
// expected-warning@+1 {{template parameter 'T' not found in the template declaration}} expected-note@+1 {{did you mean 'TT'?}}
/// \tparam T Aaa
template<typename TT>
class test_tparam5 {
// expected-warning@+1 {{template parameter 'T' not found in the template declaration}} expected-note@+1 {{did you mean 'TTT'?}}
/// \tparam T Aaa
template<typename TTT>
void test_tparam6(TTT aaa);
};
/// \tparam T1 Aaa
/// \tparam T2 Bbb
template<typename T1, typename T2>
void test_tparam7(T1 aaa, T2 bbb);
// expected-warning@+1 {{template parameter 'SomTy' not found in the template declaration}} expected-note@+1 {{did you mean 'SomeTy'?}}
/// \tparam SomTy Aaa
/// \tparam OtherTy Bbb
template<typename SomeTy, typename OtherTy>
void test_tparam8(SomeTy aaa, OtherTy bbb);
// expected-warning@+2 {{template parameter 'T1' is already documented}} expected-note@+1 {{previous documentation}}
/// \tparam T1 Aaa
/// \tparam T1 Bbb
template<typename T1, typename T2>
void test_tparam9(T1 aaa, T2 bbb);
/// \tparam T Aaa
/// \tparam TT Bbb
template<template<typename T> class TT>
void test_tparam10(TT<int> aaa);
/// \tparam T Aaa
/// \tparam TT Bbb
/// \tparam TTT Ccc
template<template<template<typename T> class TT, class C> class TTT>
void test_tparam11();
/// \tparam I Aaa
template<int I>
void test_tparam12();
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
int test1; ///< \brief\brief Aaa
@ -292,6 +360,125 @@ namespace test_attach24 {
}
}
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<typename T>
void test_attach26(T aaa);
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<typename T, typename U>
void test_attach27(T aaa, U bbb);
// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
// expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<>
void test_attach27(int aaa, int bbb);
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<typename T>
class test_attach28 {
T aaa;
};
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<typename T, typename U>
class test_attach29 { };
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<typename T>
class test_attach29<T, int> { };
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
/// \brief\brief Aaa
template<>
class test_attach29<int, int> { };
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<typename T, typename U, typename V>
class test_attach30 { };
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<typename T, typename U>
class test_attach30<T, U, int> { };
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<typename T>
class test_attach30<T, int, int> { };
// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
// expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<>
class test_attach30<int, int, int> { };
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
/// \brief\brief Aaa
class test_attach31 {
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<typename T, typename U>
void test_attach32(T aaa, U bbb);
};
template<typename T>
class test_attach33 {
// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
// expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<typename TT, typename UU>
void test_attach34(TT aaa, UU bbb);
};
// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
// expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<> template<>
void test_attach33<int>::test_attach34(int aaa, int bbb) {}
template<typename T>
class test_attach35 {
// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
// expected-warning@+2 {{'\tparam' command used in a comment that is not attached to a template declaration}}
/// \brief\brief Aaa
/// \tparam T Aaa
void test_attach36(int aaa, int bbb);
};
// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<typename T>
void test_attach35<T>::test_attach36(int aaa, int bbb) {}
// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
// expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
/// \brief\brief Aaa
/// \tparam T Aaa
template<>
void test_attach35<int>::test_attach36(int aaa, int bbb) {}
// PR13411, reduced. We used to crash on this.
/**
* @code Aaa.

View File

@ -378,6 +378,23 @@ static void DumpCXCommentInternal(struct CommentASTDumpingContext *Ctx,
else
printf(" ParamIndex=Invalid");
break;
case CXComment_TParamCommand:
printf("CXComment_TParamCommand");
PrintCXStringWithPrefixAndDispose(
"ParamName",
clang_TParamCommandComment_getParamName(Comment));
if (clang_TParamCommandComment_isParamPositionValid(Comment)) {
printf(" ParamPosition={");
for (i = 0, e = clang_TParamCommandComment_getDepth(Comment);
i != e; ++i) {
printf("%u", clang_TParamCommandComment_getIndex(Comment, i));
if (i != e - 1)
printf(", ");
}
printf("}");
} else
printf(" ParamPosition=Invalid");
break;
case CXComment_VerbatimBlockCommand:
printf("CXComment_VerbatimBlockCommand");
PrintCXStringWithPrefixAndDispose(

View File

@ -59,6 +59,9 @@ enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
case Comment::ParamCommandCommentKind:
return CXComment_ParamCommand;
case Comment::TParamCommandCommentKind:
return CXComment_TParamCommand;
case Comment::VerbatimBlockCommentKind:
return CXComment_VerbatimBlockCommand;
@ -291,6 +294,38 @@ enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
llvm_unreachable("unknown ParamCommandComment::PassDirection");
}
CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
if (!TPCC || !TPCC->hasParamName())
return createCXString((const char *) 0);
return createCXString(TPCC->getParamName(), /*DupString=*/ false);
}
unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
if (!TPCC)
return false;
return TPCC->isPositionValid();
}
unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
if (!TPCC || !TPCC->isPositionValid())
return 0;
return TPCC->getDepth();
}
unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
return 0;
return TPCC->getIndex(Depth);
}
CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
const VerbatimBlockLineComment *VBL =
getASTNodeAs<VerbatimBlockLineComment>(CXC);
@ -333,6 +368,34 @@ public:
}
};
/// This comparison will sort template parameters in the following order:
/// \li real template parameters (depth = 1) in index order;
/// \li all other names (depth > 1);
/// \li unresolved names.
class TParamCommandCommentComparePosition {
public:
bool operator()(const TParamCommandComment *LHS,
const TParamCommandComment *RHS) const {
// Sort unresolved names last.
if (!LHS->isPositionValid())
return false;
if (!RHS->isPositionValid())
return true;
if (LHS->getDepth() > 1)
return false;
if (RHS->getDepth() > 1)
return true;
// Sort template parameters in index order.
if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
return LHS->getIndex(0) < RHS->getIndex(0);
// Leave all other names in source order.
return true;
}
};
class CommentASTToHTMLConverter :
public ConstCommentVisitor<CommentASTToHTMLConverter> {
public:
@ -349,6 +412,7 @@ public:
void visitParagraphComment(const ParagraphComment *C);
void visitBlockCommandComment(const BlockCommandComment *C);
void visitParamCommandComment(const ParamCommandComment *C);
void visitTParamCommandComment(const TParamCommandComment *C);
void visitVerbatimBlockComment(const VerbatimBlockComment *C);
void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
void visitVerbatimLineComment(const VerbatimLineComment *C);
@ -486,6 +550,34 @@ void CommentASTToHTMLConverter::visitParamCommandComment(
Result << "</dd>";
}
void CommentASTToHTMLConverter::visitTParamCommandComment(
const TParamCommandComment *C) {
if (C->isPositionValid()) {
if (C->getDepth() == 1)
Result << "<dt class=\"taram-name-index-"
<< C->getIndex(0)
<< "\">";
else
Result << "<dt class=\"taram-name-index-other\">";
} else
Result << "<dt class=\"tparam-name-index-invalid\">";
Result << C->getParamName() << "</dt>";
if (C->isPositionValid()) {
if (C->getDepth() == 1)
Result << "<dd class=\"tparam-descr-index-"
<< C->getIndex(0)
<< "\">";
else
Result << "<dd class=\"tparam-descr-index-other\">";
} else
Result << "<dd class=\"tparam-descr-index-invalid\">";
visitNonStandaloneParagraphComment(C->getParagraph());
Result << "</dd>";
}
void CommentASTToHTMLConverter::visitVerbatimBlockComment(
const VerbatimBlockComment *C) {
unsigned NumLines = C->getNumLines();
@ -518,6 +610,7 @@ void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
const ParagraphComment *FirstParagraph = NULL;
const BlockCommandComment *Returns = NULL;
SmallVector<const ParamCommandComment *, 8> Params;
SmallVector<const TParamCommandComment *, 4> TParams;
SmallVector<const BlockContentComment *, 8> MiscBlocks;
// Extract various blocks into separate variables and vectors above.
@ -568,6 +661,15 @@ void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
break;
}
case Comment::TParamCommandCommentKind: {
const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
if (!TPCC->hasParamName())
break;
TParams.push_back(TPCC);
break;
}
case Comment::VerbatimBlockCommentKind:
case Comment::VerbatimLineCommentKind:
MiscBlocks.push_back(cast<BlockCommandComment>(Child));
@ -590,6 +692,9 @@ void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
std::stable_sort(Params.begin(), Params.end(),
ParamCommandCommentCompareIndex());
std::stable_sort(TParams.begin(), TParams.end(),
TParamCommandCommentComparePosition());
bool FirstParagraphIsBrief = false;
if (Brief)
visit(Brief);
@ -607,6 +712,13 @@ void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
visit(C);
}
if (TParams.size() != 0) {
Result << "<dl>";
for (unsigned i = 0, e = TParams.size(); i != e; ++i)
visit(TParams[i]);
Result << "</dl>";
}
if (Params.size() != 0) {
Result << "<dl>";
for (unsigned i = 0, e = Params.size(); i != e; ++i)

View File

@ -42,6 +42,10 @@ clang_ParamCommandComment_isParamIndexValid
clang_ParamCommandComment_getParamIndex
clang_ParamCommandComment_isDirectionExplicit
clang_ParamCommandComment_getDirection
clang_TParamCommandComment_getParamName
clang_TParamCommandComment_isParamPositionValid
clang_TParamCommandComment_getDepth
clang_TParamCommandComment_getIndex
clang_VerbatimBlockLineComment_getText
clang_VerbatimLineComment_getText
clang_HTMLTagComment_getAsString

View File

@ -221,6 +221,39 @@ template <typename T>
return ::testing::AssertionSuccess();
}
::testing::AssertionResult HasTParamCommandAt(
const Comment *C,
size_t Idx,
TParamCommandComment *&TPCC,
StringRef CommandName,
StringRef ParamName,
ParagraphComment *&Paragraph) {
::testing::AssertionResult AR = GetChildAt(C, Idx, TPCC);
if (!AR)
return AR;
StringRef ActualCommandName = TPCC->getCommandName();
if (ActualCommandName != CommandName)
return ::testing::AssertionFailure()
<< "TParamCommandComment has name \"" << ActualCommandName.str() << "\", "
"expected \"" << CommandName.str() << "\"";
if (!TPCC->hasParamName())
return ::testing::AssertionFailure()
<< "TParamCommandComment has no parameter name";
StringRef ActualParamName = TPCC->getParamName();
if (ActualParamName != ParamName)
return ::testing::AssertionFailure()
<< "TParamCommandComment has parameter name \"" << ActualParamName.str()
<< "\", "
"expected \"" << ParamName.str() << "\"";
Paragraph = TPCC->getParagraph();
return ::testing::AssertionSuccess();
}
::testing::AssertionResult HasInlineCommandAt(const Comment *C,
size_t Idx,
InlineCommandComment *&ICC,
@ -838,6 +871,33 @@ TEST_F(CommentParserTest, ParamCommand6) {
}
}
TEST_F(CommentParserTest, TParamCommand1) {
const char *Sources[] = {
"// \\tparam aaa Bbb\n",
"// \\tparam\n"
"// aaa Bbb\n",
"// \\tparam \n"
"// aaa Bbb\n",
"// \\tparam aaa\n"
"// Bbb\n"
};
for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
FullComment *FC = parseString(Sources[i]);
ASSERT_TRUE(HasChildCount(FC, 2));
ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
{
TParamCommandComment *TPCC;
ParagraphComment *PC;
ASSERT_TRUE(HasTParamCommandAt(FC, 1, TPCC, "tparam",
"aaa", PC));
ASSERT_TRUE(HasChildCount(TPCC, 1));
ASSERT_TRUE(HasParagraphCommentAt(TPCC, 0, " Bbb"));
}
}
}
TEST_F(CommentParserTest, InlineCommand1) {
const char *Source = "// \\c";