diff options
author | Dmitri Gribenko <gribozavr@gmail.com> | 2012-07-31 22:37:06 +0000 |
---|---|---|
committer | Dmitri Gribenko <gribozavr@gmail.com> | 2012-07-31 22:37:06 +0000 |
commit | 96b098674908eaa59a9128f3305cda6fbbdad563 (patch) | |
tree | d57eea0d73104b14e3bd09e070657d2af8a29edd | |
parent | 3c394c54511b27be0ff6968f159bba3521ab3c3e (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.h | 82 | ||||
-rw-r--r-- | include/clang/AST/Comment.h | 62 | ||||
-rw-r--r-- | include/clang/AST/CommentParser.h | 16 | ||||
-rw-r--r-- | include/clang/AST/CommentSema.h | 54 | ||||
-rw-r--r-- | include/clang/Basic/CommentNodes.td | 1 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticCommentKinds.td | 21 | ||||
-rw-r--r-- | lib/AST/ASTContext.cpp | 11 | ||||
-rw-r--r-- | lib/AST/CommentBriefParser.cpp | 1 | ||||
-rw-r--r-- | lib/AST/CommentDumper.cpp | 19 | ||||
-rw-r--r-- | lib/AST/CommentParser.cpp | 42 | ||||
-rw-r--r-- | lib/AST/CommentSema.cpp | 281 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 6 | ||||
-rw-r--r-- | test/Index/annotate-comments.cpp | 123 | ||||
-rw-r--r-- | test/Sema/warn-documentation.cpp | 191 | ||||
-rw-r--r-- | tools/c-index-test/c-index-test.c | 17 | ||||
-rw-r--r-- | tools/libclang/CXComment.cpp | 112 | ||||
-rw-r--r-- | tools/libclang/libclang.exports | 4 | ||||
-rw-r--r-- | unittests/AST/CommentParser.cpp | 60 |
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 > |