From ae99b75fbbac1deaccdcc1b326b8fb6b07a1e72d Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Fri, 20 Jul 2012 21:34:34 +0000 Subject: [PATCH] Add libclang APIs to walk comments ASTs and an API to convert a comment to an HTML fragment. For testing, c-index-test now has even more output: * HTML rendering of a comment * comment AST tree dump in S-expressions like Comment::dump(), but implemented * with libclang APIs. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160577 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang-c/Index.h | 401 +++++++++++++++++- include/clang/AST/Comment.h | 4 + include/clang/AST/CommentVisitor.h | 3 + test/Index/annotate-comments.cpp | 333 +++++++++++++++ tools/c-index-test/c-index-test.c | 223 +++++++++- tools/libclang/CIndex.cpp | 12 + tools/libclang/CMakeLists.txt | 1 + tools/libclang/CXComment.cpp | 657 +++++++++++++++++++++++++++++ tools/libclang/CXComment.h | 47 +++ tools/libclang/libclang.exports | 28 ++ 10 files changed, 1686 insertions(+), 23 deletions(-) create mode 100644 tools/libclang/CXComment.cpp create mode 100644 tools/libclang/CXComment.h diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 33a92e4653..d95f010a6f 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -2036,6 +2036,13 @@ typedef struct { void *data[3]; } CXCursor; +/** + * \brief A comment AST node. + */ +typedef struct { + const void *Data; +} CXComment; + /** * \defgroup CINDEX_CURSOR_MANIP Cursor manipulations * @@ -3170,11 +3177,401 @@ CINDEX_LINKAGE CXSourceRange clang_Cursor_getCommentRange(CXCursor C); CINDEX_LINKAGE CXString clang_Cursor_getRawCommentText(CXCursor C); /** - * \brief Given a cursor that represents a declaration, return the associated - * \\brief paragraph; otherwise return the first paragraph. + * \brief Given a cursor that represents a documentable entity (e.g., + * declaration), return the associated \\brief paragraph; otherwise return the + * first paragraph. */ CINDEX_LINKAGE CXString clang_Cursor_getBriefCommentText(CXCursor C); +/** + * \brief Given a cursor that represents a documentable entity (e.g., + * declaration), return the associated parsed comment as a + * \c CXComment_FullComment AST node. + */ +CINDEX_LINKAGE CXComment clang_Cursor_getParsedComment(CXCursor C); + +/** + * @} + */ + +/** + * \defgroup CINDEX_COMMENT Comment AST introspection + * + * The routines in this group provide access to information in the + * documentation comment ASTs. + * + * @{ + */ + +/** + * \brief Describes the type of the comment AST node (\c CXComment). A comment + * node can be considered block content (e. g., paragraph), inline content + * (plain text) or neither (the root AST node). + */ +enum CXCommentKind { + /** + * \brief Null comment. No AST node is constructed at the requested location + * because there is no text or a syntax error. + */ + CXComment_Null = 0, + + /** + * \brief Plain text. Inline content. + */ + CXComment_Text = 1, + + /** + * \brief A command with word-like arguments that is considered inline content. + * + * For example: \\c command. + */ + CXComment_InlineCommand = 2, + + /** + * \brief HTML start tag with attributes (name-value pairs). Considered + * inline content. + * + * For example: + * \verbatim + *

+ * \endverbatim + */ + CXComment_HTMLStartTag = 3, + + /** + * \brief HTML end tag. Considered inline content. + * + * For example: + * \verbatim + * + * \endverbatim + */ + CXComment_HTMLEndTag = 4, + + /** + * \brief A paragraph, contains inline comment. The paragraph itself is + * block content. + */ + CXComment_Paragraph = 5, + + /** + * \brief A command that has zero or more word-like arguments (number of + * word-like arguments depends on command name) and a paragraph as an + * argument. Block command is block content. + * + * Paragraph argument is also a child of the block command. + * + * For example: \\brief has 0 word-like arguments and a paragraph argument. + * + * AST nodes of special kinds that parser knows about (e. g., \\param + * command) have their own node kinds. + */ + CXComment_BlockCommand = 6, + + /** + * \brief A \\param or \\arg command that describes the function parameter + * (name, passing direction, description). + * + * \brief For example: \\param [in] ParamName description. + */ + CXComment_ParamCommand = 7, + + /** + * \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). + * + * For example: + * \\verbatim + * aaa + * \\endverbatim + */ + CXComment_VerbatimBlockCommand = 8, + + /** + * \brief A line of text that is contained within a + * CXComment_VerbatimBlockCommand node. + */ + CXComment_VerbatimBlockLine = 9, + + /** + * \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, + + /** + * \brief A full comment attached to a declaration, contains block content. + */ + CXComment_FullComment = 11 +}; + +/** + * \brief Describes parameter passing direction for \\param or \\arg command. + */ +enum CXCommentParamPassDirection { + /** + * \brief The parameter is an input parameter. + */ + CXCommentParamPassDirection_In, + + /** + * \brief The parameter is an output parameter. + */ + CXCommentParamPassDirection_Out, + + /** + * \brief The parameter is an input and output parameter. + */ + CXCommentParamPassDirection_InOut +}; + +/** + * \param Comment AST node of any kind. + * + * \returns the type of the AST node. + */ +CINDEX_LINKAGE enum CXCommentKind clang_Comment_getKind(CXComment Comment); + +/** + * \param Comment AST node of any kind. + * + * \returns number of children of the AST node. + */ +CINDEX_LINKAGE unsigned clang_Comment_getNumChildren(CXComment Comment); + +/** + * \param Comment AST node of any kind. + * + * \param ArgIdx argument index (zero-based). + * + * \returns the specified child of the AST node. + */ +CINDEX_LINKAGE +CXComment clang_Comment_getChild(CXComment Comment, unsigned ChildIdx); + +/** + * \brief A \c CXComment_Paragraph node is considered whitespace if it contains + * only \c CXComment_Text nodes that are empty or whitespace. + * + * Other AST nodes (except \c CXComment_Paragraph and \c CXComment_Text) are + * never considered whitespace. + * + * \returns non-zero if \c Comment is whitespace. + */ +CINDEX_LINKAGE unsigned clang_Comment_isWhitespace(CXComment Comment); + +/** + * \returns non-zero if \c Comment is inline content and has a newline + * immediately following it in the comment text. Newlines between paragraphs + * do not count. + */ +CINDEX_LINKAGE +unsigned clang_InlineContentComment_hasTrailingNewline(CXComment Comment); + +/** + * \param Comment a \c CXComment_Text AST node. + * + * \returns text contained in the AST node. + */ +CINDEX_LINKAGE CXString clang_TextComment_getText(CXComment Comment); + +/** + * \param Comment a \c CXComment_InlineCommand AST node. + * + * \returns name of the inline command. + */ +CINDEX_LINKAGE +CXString clang_InlineCommandComment_getCommandName(CXComment Comment); + +/** + * \param Comment a \c CXComment_InlineCommand AST node. + * + * \returns number of command arguments. + */ +CINDEX_LINKAGE +unsigned clang_InlineCommandComment_getNumArgs(CXComment Comment); + +/** + * \param Comment a \c CXComment_InlineCommand AST node. + * + * \param ArgIdx argument index (zero-based). + * + * \returns text of the specified argument. + */ +CINDEX_LINKAGE +CXString clang_InlineCommandComment_getArgText(CXComment Comment, + unsigned ArgIdx); + +/** + * \param Comment a \c CXComment_HTMLStartTag or \c CXComment_HTMLEndTag AST + * node. + * + * \returns HTML tag name. + */ +CINDEX_LINKAGE CXString clang_HTMLTagComment_getTagName(CXComment Comment); + +/** + * \param Comment a \c CXComment_HTMLStartTag AST node. + * + * \returns non-zero if tag is self-closing (for example, <br />). + */ +CINDEX_LINKAGE +unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment Comment); + +/** + * \param Comment a \c CXComment_HTMLStartTag AST node. + * + * \returns number of attributes (name-value pairs) attached to the start tag. + */ +CINDEX_LINKAGE unsigned clang_HTMLStartTag_getNumAttrs(CXComment Comment); + +/** + * \param Comment a \c CXComment_HTMLStartTag AST node. + * + * \param AttrIdx attribute index (zero-based). + * + * \returns name of the specified attribute. + */ +CINDEX_LINKAGE +CXString clang_HTMLStartTag_getAttrName(CXComment Comment, unsigned AttrIdx); + +/** + * \param Comment a \c CXComment_HTMLStartTag AST node. + * + * \param AttrIdx attribute index (zero-based). + * + * \returns value of the specified attribute. + */ +CINDEX_LINKAGE +CXString clang_HTMLStartTag_getAttrValue(CXComment Comment, unsigned AttrIdx); + +/** + * \param Comment a \c CXComment_BlockCommand AST node. + * + * \returns name of the block command. + */ +CINDEX_LINKAGE +CXString clang_BlockCommandComment_getCommandName(CXComment Comment); + +/** + * \param Comment a \c CXComment_BlockCommand AST node. + * + * \returns number of word-like arguments. + */ +CINDEX_LINKAGE +unsigned clang_BlockCommandComment_getNumArgs(CXComment Comment); + +/** + * \param Comment a \c CXComment_BlockCommand AST node. + * + * \param ArgIdx argument index (zero-based). + * + * \returns text of the specified word-like argument. + */ +CINDEX_LINKAGE +CXString clang_BlockCommandComment_getArgText(CXComment Comment, + unsigned ArgIdx); + +/** + * \param Comment a \c CXComment_BlockCommand or + * \c CXComment_VerbatimBlockCommand AST node. + * + * \returns paragraph argument of the block command. + */ +CINDEX_LINKAGE +CXComment clang_BlockCommandComment_getParagraph(CXComment Comment); + +/** + * \param Comment a \c CXComment_ParamCommand AST node. + * + * \returns parameter name. + */ +CINDEX_LINKAGE +CXString clang_ParamCommandComment_getParamName(CXComment Comment); + +/** + * \param Comment a \c CXComment_ParamCommand AST node. + * + * \returns non-zero if the parameter that this AST node represents was found + * in the function prototype and \c clang_ParamCommandComment_getParamIndex + * function will return a meaningful value. + */ +CINDEX_LINKAGE +unsigned clang_ParamCommandComment_isParamIndexValid(CXComment Comment); + +/** + * \param Comment a \c CXComment_ParamCommand AST node. + * + * \returns zero-based parameter index in function prototype. + */ +CINDEX_LINKAGE +unsigned clang_ParamCommandComment_getParamIndex(CXComment Comment); + +/** + * \param Comment a \c CXComment_ParamCommand AST node. + * + * \returns non-zero if parameter passing direction was specified explicitly in + * the comment. + */ +CINDEX_LINKAGE +unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment Comment); + +/** + * \param Comment a \c CXComment_ParamCommand AST node. + * + * \returns parameter passing direction. + */ +CINDEX_LINKAGE +enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection( + CXComment Comment); + +/** + * \param Comment a \c CXComment_VerbatimBlockLine AST node. + * + * \returns text contained in the AST node. + */ +CINDEX_LINKAGE +CXString clang_VerbatimBlockLineComment_getText(CXComment Comment); + +/** + * \param Comment a \c CXComment_VerbatimLine AST node. + * + * \returns text contained in the AST node. + */ +CINDEX_LINKAGE CXString clang_VerbatimLineComment_getText(CXComment Comment); + +/** + * \brief Convert an HTML tag AST node to string. + * + * \param Comment a \c CXComment_HTMLStartTag or \c CXComment_HTMLEndTag AST + * node. + * + * \returns string containing an HTML tag. + */ +CINDEX_LINKAGE CXString clang_HTMLTagComment_getAsString(CXComment Comment); + +/** + * \brief Convert a given full parsed comment to an HTML fragment. + * + * Specific details of HTML layout are subject to change. Don't try to parse + * this HTML back into an AST, use other APIs instead. + * + * Currently the following CSS classes are used: + * \li "para-brief" for \\brief paragraph and equivalent commands; + * \li "para-returns" for \\returns paragraph and equivalent commands; + * \li "word-returns" for the "Returns" word in \\returns paragraph. + * + * Argument list is rendered as \ list with arguments sorted in function + * prototype order. + * + * \param Comment a \c CXComment_FullComment AST node. + * + * \returns string containing an HTML fragment. + */ +CINDEX_LINKAGE CXString clang_FullComment_getAsHTML(CXComment Comment); + /** * @} */ diff --git a/include/clang/AST/Comment.h b/include/clang/AST/Comment.h index f59b5e504a..3eee704be0 100644 --- a/include/clang/AST/Comment.h +++ b/include/clang/AST/Comment.h @@ -613,6 +613,10 @@ public: return Paragraph; } + bool hasNonWhitespaceParagraph() const { + return Paragraph && !Paragraph->isWhitespace(); + } + void setParagraph(ParagraphComment *PC) { Paragraph = PC; SourceLocation NewLocEnd = PC->getLocEnd(); diff --git a/include/clang/AST/CommentVisitor.h b/include/clang/AST/CommentVisitor.h index 159725af26..47867a634f 100644 --- a/include/clang/AST/CommentVisitor.h +++ b/include/clang/AST/CommentVisitor.h @@ -24,6 +24,9 @@ public: return static_cast(this)->visit ## NAME(static_cast(C)) RetTy visit(PTR(Comment) C) { + if (!C) + return RetTy(); + switch (C->getCommentKind()) { default: llvm_unreachable("Unknown comment kind!"); #define ABSTRACT_COMMENT(COMMENT) diff --git a/test/Index/annotate-comments.cpp b/test/Index/annotate-comments.cpp index afce06a1da..9ed1c30c49 100644 --- a/test/Index/annotate-comments.cpp +++ b/test/Index/annotate-comments.cpp @@ -221,6 +221,100 @@ void isdoxy49(void); /// \returns ddd IS_DOXYGEN_END void isdoxy50(int); +/// Aaa. +void comment_to_html_conversion_1(); + +/// \brief Aaa. +void comment_to_html_conversion_2(); + +/// \short Aaa. +void comment_to_html_conversion_3(); + +/// Aaa. +/// +/// \brief Bbb. +void comment_to_html_conversion_4(); + +/// Aaa. +/// +/// \brief Bbb. +/// +/// Ccc. +void comment_to_html_conversion_5(); + +/// \brief Aaa. +/// \brief Bbb. +void comment_to_html_conversion_6(); + +/// Aaa. +/// +/// \return Bbb. +void comment_to_html_conversion_7(); + +/// Aaa. +/// +/// \returns Bbb. +void comment_to_html_conversion_8(); + +/// \returns Aaa. +/// \returns Bbb. +void comment_to_html_conversion_9(); + +/// Aaa. +/// +/// Bbb. +/// +/// \returns Ccc. +void comment_to_html_conversion_10(); + +/// \param x1 Aaa. +void comment_to_html_conversion_11(int x1); + +/// \param zzz Aaa. +void comment_to_html_conversion_12(int x1); + +/// \param x2 Bbb. +/// \param x1 Aaa. +void comment_to_html_conversion_13(int x1, int x2); + +/// \param x2 Bbb. +/// \param zzz Aaa. +/// \param x1 Aaa. +void comment_to_html_conversion_14(int x1, int x2); + +/// \brief Aaa. +/// +/// Bbb. +/// +/// \param x2 Ddd. +/// \param x1 Ccc. +/// \returns Eee. +void comment_to_html_conversion_15(int x1, int x2); + +///
Aaa +void comment_to_html_conversion_16(); + +/// \verbatim +/// Aaa +/// Aaa +/// \endverbatim +void comment_to_html_conversion_17(); + +/// \b Aaa +void comment_to_html_conversion_18(); + +/// \c Aaa \p Bbb +void comment_to_html_conversion_19(); + +/// \a Aaa \e Bbb \em Ccc +void comment_to_html_conversion_20(); + +/// \\ \@ \& \$ \# \< \> \% \" \. \:: +void comment_to_html_conversion_21(); + +/// & < > " +void comment_to_html_conversion_22(); + #endif // RUN: rm -rf %t @@ -290,3 +384,242 @@ void isdoxy50(int); // CHECK: annotate-comments.cpp:218:6: FunctionDecl=isdoxy49:{{.*}} BriefComment=[IS_DOXYGEN_START Aaa] // CHECK: annotate-comments.cpp:222:6: FunctionDecl=isdoxy50:{{.*}} BriefComment=[Returns ddd IS_DOXYGEN_END] +// CHECK: annotate-comments.cpp:225:6: FunctionDecl=comment_to_html_conversion_1:{{.*}} FullCommentAsHTML=[

Aaa.

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.])))] +// CHECK: annotate-comments.cpp:228:6: FunctionDecl=comment_to_html_conversion_2:{{.*}} FullCommentAsHTML=[

Aaa.

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[brief] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))] +// CHECK: annotate-comments.cpp:231:6: FunctionDecl=comment_to_html_conversion_3:{{.*}} FullCommentAsHTML=[

Aaa.

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[short] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))] +// CHECK: annotate-comments.cpp:236:6: FunctionDecl=comment_to_html_conversion_4:{{.*}} FullCommentAsHTML=[

Bbb.

Aaa.

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.])) +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[brief] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))] +// CHECK: annotate-comments.cpp:243:6: FunctionDecl=comment_to_html_conversion_5:{{.*}} FullCommentAsHTML=[

Bbb.

Aaa.

Ccc.

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.])) +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[brief] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))) +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Ccc.])))] +// CHECK: annotate-comments.cpp:247:6: FunctionDecl=comment_to_html_conversion_6:{{.*}} FullCommentAsHTML=[

Aaa.

Bbb.

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[brief] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[brief] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))] +// CHECK: annotate-comments.cpp:252:6: FunctionDecl=comment_to_html_conversion_7:{{.*}} FullCommentAsHTML=[

Aaa.

Returns Bbb.

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.])) +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[return] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))] +// CHECK: annotate-comments.cpp:257:6: FunctionDecl=comment_to_html_conversion_8:{{.*}} FullCommentAsHTML=[

Aaa.

Returns Bbb.

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.])) +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[returns] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))] +// CHECK: annotate-comments.cpp:261:6: FunctionDecl=comment_to_html_conversion_9:{{.*}} FullCommentAsHTML=[

Returns Bbb.

Returns Aaa.

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[returns] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[returns] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))] +// CHECK: annotate-comments.cpp:268:6: FunctionDecl=comment_to_html_conversion_10:{{.*}} FullCommentAsHTML=[

Aaa.

Bbb.

Returns Ccc.

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.])) +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Bbb.])) +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[returns] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Ccc.]))))] +// CHECK: annotate-comments.cpp:271:6: FunctionDecl=comment_to_html_conversion_11:{{.*}} FullCommentAsHTML=[
x1
Aaa.
] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x1] ParamIndex=0 +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))] +// CHECK: annotate-comments.cpp:274:6: FunctionDecl=comment_to_html_conversion_12:{{.*}} FullCommentAsHTML=[
zzz
Aaa.
] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[zzz] ParamIndex=Invalid +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))] +// CHECK: annotate-comments.cpp:278:6: FunctionDecl=comment_to_html_conversion_13:{{.*}} FullCommentAsHTML=[
x1
Aaa.
x2
Bbb.
] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x2] ParamIndex=1 +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Bbb.] HasTrailingNewline) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))) +// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x1] ParamIndex=0 +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))] +// CHECK: annotate-comments.cpp:283:6: FunctionDecl=comment_to_html_conversion_14:{{.*}} FullCommentAsHTML=[
x1
Aaa.
x2
Bbb.
zzz
Aaa.
] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x2] ParamIndex=1 +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Bbb.] HasTrailingNewline) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))) +// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[zzz] ParamIndex=Invalid +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))) +// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x1] ParamIndex=0 +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))] +// CHECK: annotate-comments.cpp:292:6: FunctionDecl=comment_to_html_conversion_15:{{.*}} FullCommentAsHTML=[

Aaa.

Bbb.

x1
Ccc.
x2
Ddd.

Returns Eee.

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[brief] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))) +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Bbb.])) +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x2] ParamIndex=1 +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Ddd.] HasTrailingNewline) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))) +// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x1] ParamIndex=0 +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Ccc.] HasTrailingNewline) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))) +// CHECK-NEXT: (CXComment_BlockCommand CommandName=[returns] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Eee.]))))] +// CHECK: annotate-comments.cpp:295:6: FunctionDecl=comment_to_html_conversion_16:{{.*}} FullCommentAsHTML=[


Aaa

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_HTMLStartTag Name=[br]) +// 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:301:6: FunctionDecl=comment_to_html_conversion_17:{{.*}} FullCommentAsHTML=[
 <a href="http://example.com/">Aaa</a>\n <a href='http://example.com/'>Aaa</a>
] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK-NEXT: (CXComment_VerbatimBlockCommand CommandName=[verbatim] +// CHECK-NEXT: (CXComment_VerbatimBlockLine Text=[ Aaa]) +// CHECK-NEXT: (CXComment_VerbatimBlockLine Text=[ Aaa])))] +// CHECK: annotate-comments.cpp:304:6: FunctionDecl=comment_to_html_conversion_18:{{.*}} FullCommentAsHTML=[

Aaa

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_InlineCommand CommandName=[b] Arg[0]=Aaa)))] +// CHECK: annotate-comments.cpp:307:6: FunctionDecl=comment_to_html_conversion_19:{{.*}} FullCommentAsHTML=[

Aaa Bbb

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_InlineCommand CommandName=[c] Arg[0]=Aaa) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_InlineCommand CommandName=[p] Arg[0]=Bbb)))] +// CHECK: annotate-comments.cpp:310:6: FunctionDecl=comment_to_html_conversion_20:{{.*}} FullCommentAsHTML=[

Aaa Bbb Ccc

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_InlineCommand CommandName=[a] Arg[0]=Aaa) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_InlineCommand CommandName=[e] Arg[0]=Bbb) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_InlineCommand CommandName=[em] Arg[0]=Ccc)))] +// CHECK: annotate-comments.cpp:313:6: FunctionDecl=comment_to_html_conversion_21:{{.*}} FullCommentAsHTML=[

\ @ & $ # < > % " . ::

] +// CHECK-NEXT: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_Text Text=[\]) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_Text Text=[@]) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_Text Text=[&]) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_Text Text=[$]) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_Text Text=[#]) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_Text Text=[<]) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_Text Text=[>]) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_Text Text=[%]) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_Text Text=["]) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_Text Text=[.]) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_Text Text=[::])))] +// CHECK: annotate-comments.cpp:316:6: FunctionDecl=comment_to_html_conversion_22:{{.*}} FullCommentAsHTML=[

&amp; &lt; &gt; &quot;

] +// CHECK: CommentAST=[ +// CHECK: (CXComment_FullComment +// CHECK: (CXComment_Paragraph +// CHECK: (CXComment_Text Text=[ & < > "])))] + diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index b3b5b8447f..bcfbece3ff 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -183,8 +183,9 @@ int parse_remapped_files(int argc, const char **argv, int start_arg, /* Pretty-printing. */ /******************************************************************************/ -static void PrintCString(const char *Prefix, const char *CStr) { - printf(" %s=[", Prefix); +static const char *FileCheckPrefix = "CHECK"; + +static void PrintCString(const char *CStr) { if (CStr != NULL && CStr[0] != '\0') { for ( ; *CStr; ++CStr) { const char C = *CStr; @@ -198,9 +199,25 @@ static void PrintCString(const char *Prefix, const char *CStr) { } } } +} + +static void PrintCStringWithPrefix(const char *Prefix, const char *CStr) { + printf(" %s=[", Prefix); + PrintCString(CStr); printf("]"); } +static void PrintCXStringAndDispose(CXString Str) { + PrintCString(clang_getCString(Str)); + clang_disposeString(Str); +} + +static void PrintCXStringWithPrefixAndDispose(const char *Prefix, + CXString Str) { + PrintCStringWithPrefix(Prefix, clang_getCString(Str)); + clang_disposeString(Str); +} + static void PrintRange(CXSourceRange R, const char *str) { CXFile begin_file, end_file; unsigned begin_line, begin_column, end_line, end_column; @@ -233,6 +250,188 @@ static void printVersion(const char *Prefix, CXVersion Version) { printf(".%d", Version.Subminor); } +struct CommentASTDumpingContext { + int IndentLevel; +}; + +static void DumpCXCommentInternal(struct CommentASTDumpingContext *Ctx, + CXComment Comment) { + Ctx->IndentLevel++; + for (unsigned i = 0, e = Ctx->IndentLevel; i != e; ++i) + printf(" "); + + printf("("); + enum CXCommentKind Kind = clang_Comment_getKind(Comment); + switch (Kind) { + case CXComment_Null: + printf("CXComment_Null"); + break; + case CXComment_Text: + printf("CXComment_Text"); + PrintCXStringWithPrefixAndDispose("Text", + clang_TextComment_getText(Comment)); + if (clang_Comment_isWhitespace(Comment)) + printf(" IsWhitespace"); + if (clang_InlineContentComment_hasTrailingNewline(Comment)) + printf(" HasTrailingNewline"); + break; + case CXComment_InlineCommand: + printf("CXComment_InlineCommand"); + PrintCXStringWithPrefixAndDispose( + "CommandName", + clang_InlineCommandComment_getCommandName(Comment)); + for (unsigned i = 0, e = clang_InlineCommandComment_getNumArgs(Comment); + i != e; ++i) { + printf(" Arg[%u]=", i); + PrintCXStringAndDispose( + clang_InlineCommandComment_getArgText(Comment, i)); + } + if (clang_InlineContentComment_hasTrailingNewline(Comment)) + printf(" HasTrailingNewline"); + break; + case CXComment_HTMLStartTag: + printf("CXComment_HTMLStartTag"); + PrintCXStringWithPrefixAndDispose( + "Name", + clang_HTMLTagComment_getTagName(Comment)); + const unsigned NumAttrs = clang_HTMLStartTag_getNumAttrs(Comment); + if (NumAttrs != 0) { + printf(" Attrs:"); + for (unsigned i = 0; i != NumAttrs; ++i) { + printf(" "); + PrintCXStringAndDispose(clang_HTMLStartTag_getAttrName(Comment, i)); + printf("="); + PrintCXStringAndDispose(clang_HTMLStartTag_getAttrValue(Comment, i)); + } + } + if (clang_HTMLStartTagComment_isSelfClosing(Comment)) + printf(" SelfClosing"); + if (clang_InlineContentComment_hasTrailingNewline(Comment)) + printf(" HasTrailingNewline"); + break; + case CXComment_HTMLEndTag: + printf("CXComment_HTMLEndTag"); + PrintCXStringWithPrefixAndDispose( + "Name", + clang_HTMLTagComment_getTagName(Comment)); + if (clang_InlineContentComment_hasTrailingNewline(Comment)) + printf(" HasTrailingNewline"); + break; + case CXComment_Paragraph: + printf("CXComment_Paragraph"); + if (clang_Comment_isWhitespace(Comment)) + printf(" IsWhitespace"); + break; + case CXComment_BlockCommand: + printf("CXComment_BlockCommand"); + PrintCXStringWithPrefixAndDispose( + "CommandName", + clang_BlockCommandComment_getCommandName(Comment)); + for (unsigned i = 0, e = clang_BlockCommandComment_getNumArgs(Comment); + i != e; ++i) { + printf(" Arg[%u]=", i); + PrintCXStringAndDispose( + clang_BlockCommandComment_getArgText(Comment, i)); + } + break; + case CXComment_ParamCommand: + printf("CXComment_ParamCommand"); + switch (clang_ParamCommandComment_getDirection(Comment)) { + case CXCommentParamPassDirection_In: + printf(" in"); + break; + case CXCommentParamPassDirection_Out: + printf(" out"); + break; + case CXCommentParamPassDirection_InOut: + printf(" in,out"); + break; + } + if (clang_ParamCommandComment_isDirectionExplicit(Comment)) + printf(" explicitly"); + else + printf(" implicitly"); + PrintCXStringWithPrefixAndDispose( + "ParamName", + clang_ParamCommandComment_getParamName(Comment)); + if (clang_ParamCommandComment_isParamIndexValid(Comment)) + printf(" ParamIndex=%u", clang_ParamCommandComment_getParamIndex(Comment)); + else + printf(" ParamIndex=Invalid"); + break; + case CXComment_VerbatimBlockCommand: + printf("CXComment_VerbatimBlockCommand"); + PrintCXStringWithPrefixAndDispose( + "CommandName", + clang_BlockCommandComment_getCommandName(Comment)); + break; + case CXComment_VerbatimBlockLine: + printf("CXComment_VerbatimBlockLine"); + PrintCXStringWithPrefixAndDispose( + "Text", + clang_VerbatimBlockLineComment_getText(Comment)); + break; + case CXComment_VerbatimLine: + printf("CXComment_VerbatimLine"); + PrintCXStringWithPrefixAndDispose( + "Text", + clang_VerbatimLineComment_getText(Comment)); + break; + case CXComment_FullComment: + printf("CXComment_FullComment"); + break; + } + if (Kind != CXComment_Null) { + const unsigned NumChildren = clang_Comment_getNumChildren(Comment); + for (unsigned i = 0; i != NumChildren; ++i) { + printf("\n// %s: ", FileCheckPrefix); + DumpCXCommentInternal(Ctx, clang_Comment_getChild(Comment, i)); + } + } + printf(")"); + Ctx->IndentLevel--; +} + +static void DumpCXComment(CXComment Comment) { + struct CommentASTDumpingContext Ctx; + Ctx.IndentLevel = 1; + printf("\n// %s: CommentAST=[\n// %s:", FileCheckPrefix, FileCheckPrefix); + DumpCXCommentInternal(&Ctx, Comment); + printf("]"); +} + +static void PrintCursorComments(CXCursor Cursor) { + { + CXString RawComment; + const char *RawCommentCString; + CXString BriefComment; + const char *BriefCommentCString; + + RawComment = clang_Cursor_getRawCommentText(Cursor); + RawCommentCString = clang_getCString(RawComment); + if (RawCommentCString != NULL && RawCommentCString[0] != '\0') { + PrintCStringWithPrefix("RawComment", RawCommentCString); + PrintRange(clang_Cursor_getCommentRange(Cursor), "RawCommentRange"); + + BriefComment = clang_Cursor_getBriefCommentText(Cursor); + BriefCommentCString = clang_getCString(BriefComment); + if (BriefCommentCString != NULL && BriefCommentCString[0] != '\0') + PrintCStringWithPrefix("BriefComment", BriefCommentCString); + clang_disposeString(BriefComment); + } + clang_disposeString(RawComment); + } + + { + CXComment Comment = clang_Cursor_getParsedComment(Cursor); + if (clang_Comment_getKind(Comment) != CXComment_Null) { + PrintCXStringWithPrefixAndDispose("FullCommentAsHTML", + clang_FullComment_getAsHTML(Comment)); + DumpCXComment(Comment); + } + } +} + static void PrintCursor(CXCursor Cursor) { CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor); if (clang_isInvalid(Cursor.kind)) { @@ -257,10 +456,6 @@ static void PrintCursor(CXCursor Cursor) { CXPlatformAvailability PlatformAvailability[2]; int NumPlatformAvailability; int I; - CXString RawComment; - const char *RawCommentCString; - CXString BriefComment; - const char *BriefCommentCString; ks = clang_getCursorKindSpelling(Cursor.kind); string = want_display_name? clang_getCursorDisplayName(Cursor) @@ -442,19 +637,7 @@ static void PrintCursor(CXCursor Cursor) { PrintRange(RefNameRange, "RefName"); } - RawComment = clang_Cursor_getRawCommentText(Cursor); - RawCommentCString = clang_getCString(RawComment); - if (RawCommentCString != NULL && RawCommentCString[0] != '\0') { - PrintCString("RawComment", RawCommentCString); - PrintRange(clang_Cursor_getCommentRange(Cursor), "RawCommentRange"); - - BriefComment = clang_Cursor_getBriefCommentText(Cursor); - BriefCommentCString = clang_getCString(BriefComment); - if (BriefCommentCString != NULL && BriefCommentCString[0] != '\0') - PrintCString("BriefComment", BriefCommentCString); - clang_disposeString(BriefComment); - } - clang_disposeString(RawComment); + PrintCursorComments(Cursor); } } @@ -577,8 +760,6 @@ void PrintMemoryUsage(CXTranslationUnit TU) { /* Logic for testing traversal. */ /******************************************************************************/ -static const char *FileCheckPrefix = "CHECK"; - static void PrintCursorExtent(CXCursor C) { CXSourceRange extent = clang_getCursorExtent(C); PrintRange(extent, "Extent"); diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 3b3b697d17..b1e4bad14d 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "CIndexer.h" +#include "CXComment.h" #include "CXCursor.h" #include "CXTranslationUnit.h" #include "CXString.h" @@ -5729,6 +5730,17 @@ CXString clang_Cursor_getBriefCommentText(CXCursor C) { return createCXString((const char *) NULL); } +CXComment clang_Cursor_getParsedComment(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return cxcomment::createCXComment(NULL); + + const Decl *D = getCursorDecl(C); + const ASTContext &Context = getCursorContext(C); + const comments::FullComment *FC = Context.getCommentForDecl(D); + + return cxcomment::createCXComment(FC); +} + } // end: extern "C" //===----------------------------------------------------------------------===// diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index 2fcbf5b75b..627003832f 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -15,6 +15,7 @@ set(SOURCES CIndexUSRs.cpp CIndexer.cpp CIndexer.h + CXComment.cpp CXCursor.cpp CXCursor.h CXCompilationDatabase.cpp diff --git a/tools/libclang/CXComment.cpp b/tools/libclang/CXComment.cpp new file mode 100644 index 0000000000..8d01a4192f --- /dev/null +++ b/tools/libclang/CXComment.cpp @@ -0,0 +1,657 @@ +//===- CXComment.cpp - libclang APIs for manipulating CXComments ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines all libclang APIs related to walking comment AST. +// +//===----------------------------------------------------------------------===// + +#include "clang-c/Index.h" +#include "CXString.h" +#include "CXComment.h" + +#include "clang/AST/CommentVisitor.h" + +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; +using namespace clang::cxstring; +using namespace clang::comments; +using namespace clang::cxcomment; + +extern "C" { + +enum CXCommentKind clang_Comment_getKind(CXComment CXC) { + const Comment *C = getASTNode(CXC); + if (!C) + return CXComment_Null; + + switch (C->getCommentKind()) { + case Comment::NoCommentKind: + return CXComment_Null; + + case Comment::TextCommentKind: + return CXComment_Text; + + case Comment::InlineCommandCommentKind: + return CXComment_InlineCommand; + + case Comment::HTMLStartTagCommentKind: + return CXComment_HTMLStartTag; + + case Comment::HTMLEndTagCommentKind: + return CXComment_HTMLEndTag; + + case Comment::ParagraphCommentKind: + return CXComment_Paragraph; + + case Comment::BlockCommandCommentKind: + return CXComment_BlockCommand; + + case Comment::ParamCommandCommentKind: + return CXComment_ParamCommand; + + case Comment::VerbatimBlockCommentKind: + return CXComment_VerbatimBlockCommand; + + case Comment::VerbatimBlockLineCommentKind: + return CXComment_VerbatimBlockLine; + + case Comment::VerbatimLineCommentKind: + return CXComment_VerbatimLine; + + case Comment::FullCommentKind: + return CXComment_FullComment; + } + llvm_unreachable("unknown CommentKind"); +} + +unsigned clang_Comment_getNumChildren(CXComment CXC) { + const Comment *C = getASTNode(CXC); + if (!C) + return 0; + + return C->child_count(); +} + +CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) { + const Comment *C = getASTNode(CXC); + if (!C || ChildIdx >= C->child_count()) + return createCXComment(NULL); + + return createCXComment(*(C->child_begin() + ChildIdx)); +} + +unsigned clang_Comment_isWhitespace(CXComment CXC) { + const Comment *C = getASTNode(CXC); + if (!C) + return false; + + if (const TextComment *TC = dyn_cast(C)) + return TC->isWhitespace(); + + if (const ParagraphComment *PC = dyn_cast(C)) + return PC->isWhitespace(); + + return false; +} + +unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) { + const InlineContentComment *ICC = getASTNodeAs(CXC); + if (!ICC) + return false; + + return ICC->hasTrailingNewline(); +} + +CXString clang_TextComment_getText(CXComment CXC) { + const TextComment *TC = getASTNodeAs(CXC); + if (!TC) + return createCXString((const char *) 0); + + return createCXString(TC->getText(), /*DupString=*/ false); +} + +CXString clang_InlineCommandComment_getCommandName(CXComment CXC) { + const InlineCommandComment *ICC = getASTNodeAs(CXC); + if (!ICC) + return createCXString((const char *) 0); + + return createCXString(ICC->getCommandName(), /*DupString=*/ false); +} + +unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) { + const InlineCommandComment *ICC = getASTNodeAs(CXC); + if (!ICC) + return 0; + + return ICC->getNumArgs(); +} + +CXString clang_InlineCommandComment_getArgText(CXComment CXC, + unsigned ArgIdx) { + const InlineCommandComment *ICC = getASTNodeAs(CXC); + if (!ICC || ArgIdx >= ICC->getNumArgs()) + return createCXString((const char *) 0); + + return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false); +} + +CXString clang_HTMLTagComment_getTagName(CXComment CXC) { + const HTMLTagComment *HTC = getASTNodeAs(CXC); + if (!HTC) + return createCXString((const char *) 0); + + return createCXString(HTC->getTagName(), /*DupString=*/ false); +} + +unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) { + const HTMLStartTagComment *HST = getASTNodeAs(CXC); + if (!HST) + return false; + + return HST->isSelfClosing(); +} + +unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) { + const HTMLStartTagComment *HST = getASTNodeAs(CXC); + if (!HST) + return 0; + + return HST->getNumAttrs(); +} + +CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) { + const HTMLStartTagComment *HST = getASTNodeAs(CXC); + if (!HST || AttrIdx >= HST->getNumAttrs()) + return createCXString((const char *) 0); + + return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false); +} + +CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) { + const HTMLStartTagComment *HST = getASTNodeAs(CXC); + if (!HST || AttrIdx >= HST->getNumAttrs()) + return createCXString((const char *) 0); + + return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false); +} + +CXString clang_BlockCommandComment_getCommandName(CXComment CXC) { + const BlockCommandComment *BCC = getASTNodeAs(CXC); + if (!BCC) + return createCXString((const char *) 0); + + return createCXString(BCC->getCommandName(), /*DupString=*/ false); +} + +unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) { + const BlockCommandComment *BCC = getASTNodeAs(CXC); + if (!BCC) + return 0; + + return BCC->getNumArgs(); +} + +CXString clang_BlockCommandComment_getArgText(CXComment CXC, + unsigned ArgIdx) { + const BlockCommandComment *BCC = getASTNodeAs(CXC); + if (!BCC || ArgIdx >= BCC->getNumArgs()) + return createCXString((const char *) 0); + + return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false); +} + +CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) { + const BlockCommandComment *BCC = getASTNodeAs(CXC); + if (!BCC) + return createCXComment(NULL); + + return createCXComment(BCC->getParagraph()); +} + +CXString clang_ParamCommandComment_getParamName(CXComment CXC) { + const ParamCommandComment *PCC = getASTNodeAs(CXC); + if (!PCC) + return createCXString((const char *) 0); + + return createCXString(PCC->getParamName(), /*DupString=*/ false); +} + +unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) { + const ParamCommandComment *PCC = getASTNodeAs(CXC); + if (!PCC) + return false; + + return PCC->isParamIndexValid(); +} + +unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) { + const ParamCommandComment *PCC = getASTNodeAs(CXC); + if (!PCC) + return ParamCommandComment::InvalidParamIndex; + + return PCC->getParamIndex(); +} + +unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) { + const ParamCommandComment *PCC = getASTNodeAs(CXC); + if (!PCC) + return false; + + return PCC->isDirectionExplicit(); +} + +enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection( + CXComment CXC) { + const ParamCommandComment *PCC = getASTNodeAs(CXC); + if (!PCC) + return CXCommentParamPassDirection_In; + + switch (PCC->getDirection()) { + case ParamCommandComment::In: + return CXCommentParamPassDirection_In; + + case ParamCommandComment::Out: + return CXCommentParamPassDirection_Out; + + case ParamCommandComment::InOut: + return CXCommentParamPassDirection_InOut; + } + llvm_unreachable("unknown ParamCommandComment::PassDirection"); +} + +CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) { + const VerbatimBlockLineComment *VBL = + getASTNodeAs(CXC); + if (!VBL) + return createCXString((const char *) 0); + + return createCXString(VBL->getText(), /*DupString=*/ false); +} + +CXString clang_VerbatimLineComment_getText(CXComment CXC) { + const VerbatimLineComment *VLC = getASTNodeAs(CXC); + if (!VLC) + return createCXString((const char *) 0); + + return createCXString(VLC->getText(), /*DupString=*/ false); +} + +} // end extern "C" + +//===----------------------------------------------------------------------===// +// Helpers for converting comment AST to HTML. +//===----------------------------------------------------------------------===// + +namespace { + +class ParamCommandCommentCompareIndex { +public: + bool operator()(const ParamCommandComment *LHS, + const ParamCommandComment *RHS) const { + // To sort invalid (unresolved) parameters last, this comparison relies on + // invalid indices to be UINT_MAX. + return LHS->getParamIndex() < RHS->getParamIndex(); + } +}; + +class CommentASTToHTMLConverter : + public ConstCommentVisitor { +public: + CommentASTToHTMLConverter() { } + + // Inline content. + void visitTextComment(const TextComment *C); + void visitInlineCommandComment(const InlineCommandComment *C); + void visitHTMLStartTagComment(const HTMLStartTagComment *C); + void visitHTMLEndTagComment(const HTMLEndTagComment *C); + + // Block content. + void visitParagraphComment(const ParagraphComment *C); + void visitBlockCommandComment(const BlockCommandComment *C); + void visitParamCommandComment(const ParamCommandComment *C); + void visitVerbatimBlockComment(const VerbatimBlockComment *C); + void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); + void visitVerbatimLineComment(const VerbatimLineComment *C); + + void visitFullComment(const FullComment *C); + + // Helpers. + + /// Convert a paragraph that is not a block by itself (an argument to some + /// command). + void visitNonStandaloneParagraphComment(const ParagraphComment *C); + + void appendToResultWithHTMLEscaping(StringRef S); + + StringRef getAsHTML() const { + return Result; + } + +private: + /// Accumulator for converted HTML. + std::string Result; +}; +} // end unnamed namespace + +void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) { + appendToResultWithHTMLEscaping(C->getText()); +} + +void CommentASTToHTMLConverter::visitInlineCommandComment( + const InlineCommandComment *C) { + StringRef CommandName = C->getCommandName(); + bool HasArg0 = C->getNumArgs() > 0 && !C->getArgText(0).empty(); + StringRef Arg0; + if (HasArg0) + Arg0 = C->getArgText(0); + + if (CommandName == "b") { + if (!HasArg0) + return; + Result += ""; + Result += Arg0; + Result += ""; + return; + } + if (CommandName == "c" || CommandName == "p") { + if (!HasArg0) + return; + Result += ""; + Result += Arg0; + Result += ""; + return; + } + if (CommandName == "a" || CommandName == "e" || CommandName == "em") { + if (!HasArg0) + return; + Result += ""; + Result += Arg0; + Result += ""; + return; + } + + // We don't recognize this command, so just print its arguments. + for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { + Result += C->getArgText(i); + Result += " "; + } +} + +void CommentASTToHTMLConverter::visitHTMLStartTagComment( + const HTMLStartTagComment *C) { + Result += "<"; + Result += C->getTagName(); + + if (C->getNumAttrs() != 0) { + for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) { + Result += " "; + const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); + Result += Attr.Name; + if (!Attr.Value.empty()) { + Result += "=\""; + Result += Attr.Value; + Result += " \""; + } + } + } + + if (!C->isSelfClosing()) + Result += ">"; + else + Result += "/>"; +} + +void CommentASTToHTMLConverter::visitHTMLEndTagComment( + const HTMLEndTagComment *C) { + Result += "getTagName(); + Result += ">"; +} + +void CommentASTToHTMLConverter::visitParagraphComment( + const ParagraphComment *C) { + if (C->isWhitespace()) + return; + + Result += "

"; + for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); + I != E; ++I) { + visit(*I); + } + Result += "

"; +} + +void CommentASTToHTMLConverter::visitBlockCommandComment( + const BlockCommandComment *C) { + StringRef CommandName = C->getCommandName(); + if (CommandName == "brief" || CommandName == "short") { + Result += "

"; + visitNonStandaloneParagraphComment(C->getParagraph()); + Result += "

"; + return; + } + if (CommandName == "returns" || CommandName == "return") { + Result += "

"; + Result += "Returns "; + visitNonStandaloneParagraphComment(C->getParagraph()); + Result += "

"; + return; + } + // We don't know anything about this command. Just render the paragraph. + visit(C->getParagraph()); +} + +void CommentASTToHTMLConverter::visitParamCommandComment( + const ParamCommandComment *C) { + Result += "
"; + Result += C->getParamName(); + Result += "
"; + Result += "
"; + visitNonStandaloneParagraphComment(C->getParagraph()); + Result += "
"; +} + +void CommentASTToHTMLConverter::visitVerbatimBlockComment( + const VerbatimBlockComment *C) { + unsigned NumLines = C->getNumLines(); + if (NumLines == 0) + return; + + Result += "
";
+  for (unsigned i = 0; i != NumLines; ++i) {
+    appendToResultWithHTMLEscaping(C->getText(i));
+    if (i + 1 != NumLines)
+      Result.append("\n");
+  }
+  Result += "
"; +} + +void CommentASTToHTMLConverter::visitVerbatimBlockLineComment( + const VerbatimBlockLineComment *C) { + llvm_unreachable("should not see this AST node"); +} + +void CommentASTToHTMLConverter::visitVerbatimLineComment( + const VerbatimLineComment *C) { + Result += "
";
+  appendToResultWithHTMLEscaping(C->getText());
+  Result += "
"; +} + +void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) { + const BlockContentComment *Brief = NULL; + const ParagraphComment *FirstParagraph = NULL; + const BlockCommandComment *Returns = NULL; + SmallVector Params; + SmallVector MiscBlocks; + + // Extract various blocks into separate variables and vectors above. + for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); + I != E; ++I) { + const Comment *Child = *I; + if (!Child) + continue; + switch (Child->getCommentKind()) { + case Comment::NoCommentKind: + continue; + + case Comment::ParagraphCommentKind: { + const ParagraphComment *PC = cast(Child); + if (PC->isWhitespace()) + break; + if (!FirstParagraph) + FirstParagraph = PC; + + MiscBlocks.push_back(PC); + break; + } + + case Comment::BlockCommandCommentKind: { + const BlockCommandComment *BCC = cast(Child); + StringRef CommandName = BCC->getCommandName(); + if (!Brief && (CommandName == "brief" || CommandName == "short")) { + Brief = BCC; + break; + } + if (!Returns && (CommandName == "returns" || CommandName == "return")) { + Returns = BCC; + break; + } + MiscBlocks.push_back(BCC); + break; + } + + case Comment::ParamCommandCommentKind: { + const ParamCommandComment *PCC = cast(Child); + if (!PCC->hasParamName()) + break; + + if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph()) + break; + + Params.push_back(PCC); + break; + } + + case Comment::VerbatimBlockCommentKind: + case Comment::VerbatimLineCommentKind: + MiscBlocks.push_back(cast(Child)); + break; + + case Comment::TextCommentKind: + case Comment::InlineCommandCommentKind: + case Comment::HTMLStartTagCommentKind: + case Comment::HTMLEndTagCommentKind: + case Comment::VerbatimBlockLineCommentKind: + case Comment::FullCommentKind: + llvm_unreachable("AST node of this kind can't be a child of " + "a FullComment"); + } + } + + // Sort params in order they are declared in the function prototype. + // Unresolved parameters are put at the end of the list in the same order + // they were seen in the comment. + std::stable_sort(Params.begin(), Params.end(), + ParamCommandCommentCompareIndex()); + + bool FirstParagraphIsBrief = false; + if (Brief) + visit(Brief); + else if (FirstParagraph) { + Result += "

"; + visitNonStandaloneParagraphComment(FirstParagraph); + Result += "

"; + FirstParagraphIsBrief = true; + } + + for (unsigned i = 0, e = MiscBlocks.size(); i != e; ++i) { + const Comment *C = MiscBlocks[i]; + if (FirstParagraphIsBrief && C == FirstParagraph) + continue; + visit(C); + } + + if (Params.size() != 0) { + Result += "
"; + for (unsigned i = 0, e = Params.size(); i != e; ++i) + visit(Params[i]); + Result += "
"; + } + + if (Returns) + visit(Returns); +} + +void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment( + const ParagraphComment *C) { + if (!C) + return; + + for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); + I != E; ++I) { + visit(*I); + } +} + +void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) { + Result.reserve(Result.size() + S.size()); + for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { + const char C = *I; + switch (C) { + case '&': + Result.append("&"); + break; + case '<': + Result.append("<"); + break; + case '>': + Result.append(">"); + break; + case '"': + Result.append("""); + break; + case '\'': + Result.append("'"); + break; + case '/': + Result.append("/"); + break; + default: + Result.push_back(C); + break; + } + } +} + +extern "C" { + +CXString clang_HTMLTagComment_getAsString(CXComment CXC) { + const HTMLTagComment *HTC = getASTNodeAs(CXC); + if (!HTC) + return createCXString((const char *) 0); + + CommentASTToHTMLConverter Converter; + Converter.visit(HTC); + return createCXString(Converter.getAsHTML()); +} + +CXString clang_FullComment_getAsHTML(CXComment CXC) { + const FullComment *FC = getASTNodeAs(CXC); + if (!FC) + return createCXString((const char *) 0); + + CommentASTToHTMLConverter Converter; + Converter.visit(FC); + return createCXString(Converter.getAsHTML()); +} + +} // end extern "C" + diff --git a/tools/libclang/CXComment.h b/tools/libclang/CXComment.h new file mode 100644 index 0000000000..753877e6c7 --- /dev/null +++ b/tools/libclang/CXComment.h @@ -0,0 +1,47 @@ +//===- CXComment.h - Routines for manipulating CXComments -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXComments. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CXCOMMENT_H +#define LLVM_CLANG_CXCOMMENT_H + +#include "clang-c/Index.h" + +#include "clang/AST/Comment.h" + +namespace clang { +namespace cxcomment { + +inline CXComment createCXComment(const comments::Comment *C) { + CXComment Result; + Result.Data = C; + return Result; +} + +inline const comments::Comment *getASTNode(CXComment CXC) { + return static_cast(CXC.Data); +} + +template +inline const T *getASTNodeAs(CXComment CXC) { + const comments::Comment *C = getASTNode(CXC); + if (!C) + return NULL; + + return dyn_cast(C); +} + +} // end namespace cxcomment +} // end namespace clang + +#endif + diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index 14ded197e7..7d3b2a9c5d 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -7,6 +7,7 @@ clang_CXXMethod_isVirtual clang_Cursor_getArgument clang_Cursor_getBriefCommentText clang_Cursor_getCommentRange +clang_Cursor_getParsedComment clang_Cursor_getRawCommentText clang_Cursor_getNumArguments clang_Cursor_getObjCSelectorIndex @@ -17,6 +18,33 @@ clang_Cursor_isNull clang_IndexAction_create clang_IndexAction_dispose clang_Range_isNull +clang_Comment_getKind +clang_Comment_getNumChildren +clang_Comment_getChild +clang_Comment_isWhitespace +clang_InlineContentComment_hasTrailingNewline +clang_TextComment_getText +clang_InlineCommandComment_getCommandName +clang_InlineCommandComment_getNumArgs +clang_InlineCommandComment_getArgText +clang_HTMLTagComment_getTagName +clang_HTMLStartTagComment_isSelfClosing +clang_HTMLStartTag_getNumAttrs +clang_HTMLStartTag_getAttrName +clang_HTMLStartTag_getAttrValue +clang_BlockCommandComment_getCommandName +clang_BlockCommandComment_getNumArgs +clang_BlockCommandComment_getArgText +clang_BlockCommandComment_getParagraph +clang_ParamCommandComment_getParamName +clang_ParamCommandComment_isParamIndexValid +clang_ParamCommandComment_getParamIndex +clang_ParamCommandComment_isDirectionExplicit +clang_ParamCommandComment_getDirection +clang_VerbatimBlockLineComment_getText +clang_VerbatimLineComment_getText +clang_HTMLTagComment_getAsString +clang_FullComment_getAsHTML clang_annotateTokens clang_codeCompleteAt clang_codeCompleteGetContainerKind -- 2.40.0