aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitri Gribenko <gribozavr@gmail.com>2012-07-31 22:37:06 +0000
committerDmitri Gribenko <gribozavr@gmail.com>2012-07-31 22:37:06 +0000
commit96b098674908eaa59a9128f3305cda6fbbdad563 (patch)
treed57eea0d73104b14e3bd09e070657d2af8a29edd
parent3c394c54511b27be0ff6968f159bba3521ab3c3e (diff)
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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161087 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang-c/Index.h82
-rw-r--r--include/clang/AST/Comment.h62
-rw-r--r--include/clang/AST/CommentParser.h16
-rw-r--r--include/clang/AST/CommentSema.h54
-rw-r--r--include/clang/Basic/CommentNodes.td1
-rw-r--r--include/clang/Basic/DiagnosticCommentKinds.td21
-rw-r--r--lib/AST/ASTContext.cpp11
-rw-r--r--lib/AST/CommentBriefParser.cpp1
-rw-r--r--lib/AST/CommentDumper.cpp19
-rw-r--r--lib/AST/CommentParser.cpp42
-rw-r--r--lib/AST/CommentSema.cpp281
-rw-r--r--lib/Sema/SemaDecl.cpp1
-rw-r--r--lib/Sema/SemaTemplate.cpp6
-rw-r--r--test/Index/annotate-comments.cpp123
-rw-r--r--test/Sema/warn-documentation.cpp191
-rw-r--r--tools/c-index-test/c-index-test.c17
-rw-r--r--tools/libclang/CXComment.cpp112
-rw-r--r--tools/libclang/libclang.exports4
-rw-r--r--unittests/AST/CommentParser.cpp60
19 files changed, 1035 insertions, 69 deletions
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index c21dafd2dc..ab6c3afdb8 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -3277,6 +3277,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
* text (\c CXComment_VerbatimBlockLine child nodes).
@@ -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
};
/**
@@ -3564,6 +3572,63 @@ 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.
*
* \returns text contained in the 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.
diff --git a/include/clang/AST/Comment.h b/include/clang/AST/Comment.h
index 4c20618ca1..089e7cd8e3 100644
--- a/include/clang/AST/Comment.h
+++ b/include/clang/AST/Comment.h
@@ -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;
diff --git a/include/clang/AST/CommentParser.h b/include/clang/AST/CommentParser.h
index 43c669adba..53ea2fcdec 100644
--- a/include/clang/AST/CommentParser.h
+++ b/include/clang/AST/CommentParser.h
@@ -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,
diff --git a/include/clang/AST/CommentSema.h b/include/clang/AST/CommentSema.h
index 17d9ab7761..c51439b96f 100644
--- a/include/clang/AST/CommentSema.h
+++ b/include/clang/AST/CommentSema.h
@@ -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;
diff --git a/include/clang/Basic/CommentNodes.td b/include/clang/Basic/CommentNodes.td
index 0e3f284521..7bf32b78b5 100644
--- a/include/clang/Basic/CommentNodes.td
+++ b/include/clang/Basic/CommentNodes.td
@@ -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>;
diff --git a/include/clang/Basic/DiagnosticCommentKinds.td b/include/clang/Basic/DiagnosticCommentKinds.td
index 91aa5f19e6..4aa812ebc1 100644
--- a/include/clang/Basic/DiagnosticCommentKinds.td
+++ b/include/clang/Basic/DiagnosticCommentKinds.td
@@ -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
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 46a4d87f9a..edcfe8ea7a 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -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();
diff --git a/lib/AST/CommentBriefParser.cpp b/lib/AST/CommentBriefParser.cpp
index 47d52e19a2..22209c097f 100644
--- a/lib/AST/CommentBriefParser.cpp
+++ b/lib/AST/CommentBriefParser.cpp
@@ -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
diff --git a/lib/AST/CommentDumper.cpp b/lib/AST/CommentDumper.cpp
index f02ea334cd..dffc8233a0 100644
--- a/lib/AST/CommentDumper.cpp
+++ b/lib/AST/CommentDumper.cpp
@@ -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);
diff --git a/lib/AST/CommentParser.cpp b/lib/AST/CommentParser.cpp
index 6d535673f8..63560e197d 100644
--- a/lib/AST/CommentParser.cpp
+++ b/lib/AST/CommentParser.cpp
@@ -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
diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp
index bb0d03ebd2..7b42c861d3 100644
--- a/lib/AST/CommentSema.cpp
+++ b/lib/AST/CommentSema.cpp
@@ -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 >