Skip to content

Commit fade04f

Browse files
authoredJun 10, 2024··
[Clang][Comments] Add argument parsing for @throw @throws @exception (#84726)
Doxygen allows for the `@throw`, `@throws`, and `@exception` commands to have an attached argument indicating the type being thrown. Currently, Clang's AST parsing doesn't support parsing out this argument from doc comments. The result is missing compatibility with Doxygen. This PR implements parsing of arguments for the `@throw`, `@throws`, and `@exception` commands. Each command can only have one argument, matching the semantics of Doxygen.
1 parent 83da21a commit fade04f

File tree

6 files changed

+386
-45
lines changed

6 files changed

+386
-45
lines changed
 

‎clang/include/clang/AST/CommentCommands.td

+3-3
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,9 @@ def Tparam : BlockCommand<"tparam"> { let IsTParamCommand = 1; }
132132
// HeaderDoc command for template parameter documentation.
133133
def Templatefield : BlockCommand<"templatefield"> { let IsTParamCommand = 1; }
134134

135-
def Throws : BlockCommand<"throws"> { let IsThrowsCommand = 1; }
136-
def Throw : BlockCommand<"throw"> { let IsThrowsCommand = 1; }
137-
def Exception : BlockCommand<"exception"> { let IsThrowsCommand = 1; }
135+
def Throws : BlockCommand<"throws"> { let IsThrowsCommand = 1; let NumArgs = 1; }
136+
def Throw : BlockCommand<"throw"> { let IsThrowsCommand = 1; let NumArgs = 1; }
137+
def Exception : BlockCommand<"exception"> { let IsThrowsCommand = 1; let NumArgs = 1;}
138138

139139
def Deprecated : BlockCommand<"deprecated"> {
140140
let IsEmptyParagraphAllowed = 1;

‎clang/include/clang/AST/CommentParser.h

+5
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ class Parser {
100100
ArrayRef<Comment::Argument>
101101
parseCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs);
102102

103+
/// Parse arguments for \throws command supported args are in form of class
104+
/// or template.
105+
ArrayRef<Comment::Argument>
106+
parseThrowCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs);
107+
103108
BlockCommandComment *parseBlockCommand();
104109
InlineCommandComment *parseInlineCommand();
105110

‎clang/lib/AST/CommentParser.cpp

+93
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,31 @@ class TextTokenRetokenizer {
8989
}
9090
}
9191

92+
/// Extract a template type
93+
bool lexTemplate(SmallString<32> &WordText) {
94+
unsigned BracketCount = 0;
95+
while (!isEnd()) {
96+
const char C = peek();
97+
WordText.push_back(C);
98+
consumeChar();
99+
switch (C) {
100+
case '<': {
101+
BracketCount++;
102+
break;
103+
}
104+
case '>': {
105+
BracketCount--;
106+
if (!BracketCount)
107+
return true;
108+
break;
109+
}
110+
default:
111+
break;
112+
}
113+
}
114+
return false;
115+
}
116+
92117
/// Add a token.
93118
/// Returns true on success, false if there are no interesting tokens to
94119
/// fetch from lexer.
@@ -149,6 +174,54 @@ class TextTokenRetokenizer {
149174
addToken();
150175
}
151176

177+
/// Extract a type argument
178+
bool lexType(Token &Tok) {
179+
if (isEnd())
180+
return false;
181+
182+
// Save current position in case we need to rollback because the type is
183+
// empty.
184+
Position SavedPos = Pos;
185+
186+
// Consume any leading whitespace.
187+
consumeWhitespace();
188+
SmallString<32> WordText;
189+
const char *WordBegin = Pos.BufferPtr;
190+
SourceLocation Loc = getSourceLocation();
191+
192+
while (!isEnd()) {
193+
const char C = peek();
194+
// For non-whitespace characters we check if it's a template or otherwise
195+
// continue reading the text into a word.
196+
if (!isWhitespace(C)) {
197+
if (C == '<') {
198+
if (!lexTemplate(WordText))
199+
return false;
200+
} else {
201+
WordText.push_back(C);
202+
consumeChar();
203+
}
204+
} else {
205+
consumeChar();
206+
break;
207+
}
208+
}
209+
210+
const unsigned Length = WordText.size();
211+
if (Length == 0) {
212+
Pos = SavedPos;
213+
return false;
214+
}
215+
216+
char *TextPtr = Allocator.Allocate<char>(Length + 1);
217+
218+
memcpy(TextPtr, WordText.c_str(), Length + 1);
219+
StringRef Text = StringRef(TextPtr, Length);
220+
221+
formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
222+
return true;
223+
}
224+
152225
/// Extract a word -- sequence of non-whitespace characters.
153226
bool lexWord(Token &Tok) {
154227
if (isEnd())
@@ -304,6 +377,23 @@ Parser::parseCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs) {
304377
return llvm::ArrayRef(Args, ParsedArgs);
305378
}
306379

380+
ArrayRef<Comment::Argument>
381+
Parser::parseThrowCommandArgs(TextTokenRetokenizer &Retokenizer,
382+
unsigned NumArgs) {
383+
auto *Args = new (Allocator.Allocate<Comment::Argument>(NumArgs))
384+
Comment::Argument[NumArgs];
385+
unsigned ParsedArgs = 0;
386+
Token Arg;
387+
388+
while (ParsedArgs < NumArgs && Retokenizer.lexType(Arg)) {
389+
Args[ParsedArgs] = Comment::Argument{
390+
SourceRange(Arg.getLocation(), Arg.getEndLocation()), Arg.getText()};
391+
ParsedArgs++;
392+
}
393+
394+
return llvm::ArrayRef(Args, ParsedArgs);
395+
}
396+
307397
BlockCommandComment *Parser::parseBlockCommand() {
308398
assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
309399

@@ -356,6 +446,9 @@ BlockCommandComment *Parser::parseBlockCommand() {
356446
parseParamCommandArgs(PC, Retokenizer);
357447
else if (TPC)
358448
parseTParamCommandArgs(TPC, Retokenizer);
449+
else if (Info->IsThrowsCommand)
450+
S.actOnBlockCommandArgs(
451+
BC, parseThrowCommandArgs(Retokenizer, Info->NumArgs));
359452
else
360453
S.actOnBlockCommandArgs(BC, parseCommandArgs(Retokenizer, Info->NumArgs));
361454

‎clang/lib/Index/CommentToXML.cpp

+23-11
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,8 @@ class CommentASTToXMLConverter :
546546
void visitParagraphComment(const ParagraphComment *C);
547547

548548
void appendParagraphCommentWithKind(const ParagraphComment *C,
549-
StringRef Kind);
549+
StringRef ParagraphKind,
550+
StringRef PrependBodyText);
550551

551552
void visitBlockCommandComment(const BlockCommandComment *C);
552553
void visitParamCommandComment(const ParamCommandComment *C);
@@ -680,24 +681,27 @@ CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
680681
Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
681682
}
682683

683-
void
684-
CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
685-
appendParagraphCommentWithKind(C, StringRef());
684+
void CommentASTToXMLConverter::visitParagraphComment(
685+
const ParagraphComment *C) {
686+
appendParagraphCommentWithKind(C, StringRef(), StringRef());
686687
}
687688

688689
void CommentASTToXMLConverter::appendParagraphCommentWithKind(
689-
const ParagraphComment *C,
690-
StringRef ParagraphKind) {
691-
if (C->isWhitespace())
690+
const ParagraphComment *C, StringRef ParagraphKind,
691+
StringRef PrependBodyText) {
692+
if (C->isWhitespace() && PrependBodyText.empty())
692693
return;
693694

694695
if (ParagraphKind.empty())
695696
Result << "<Para>";
696697
else
697698
Result << "<Para kind=\"" << ParagraphKind << "\">";
698699

699-
for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
700-
I != E; ++I) {
700+
if (!PrependBodyText.empty())
701+
Result << PrependBodyText << " ";
702+
703+
for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); I != E;
704+
++I) {
701705
visit(*I);
702706
}
703707
Result << "</Para>";
@@ -706,8 +710,15 @@ void CommentASTToXMLConverter::appendParagraphCommentWithKind(
706710
void CommentASTToXMLConverter::visitBlockCommandComment(
707711
const BlockCommandComment *C) {
708712
StringRef ParagraphKind;
713+
StringRef ExceptionType;
709714

710-
switch (C->getCommandID()) {
715+
const unsigned CommandID = C->getCommandID();
716+
const CommandInfo *Info = Traits.getCommandInfo(CommandID);
717+
if (Info->IsThrowsCommand && C->getNumArgs() > 0) {
718+
ExceptionType = C->getArgText(0);
719+
}
720+
721+
switch (CommandID) {
711722
case CommandTraits::KCI_attention:
712723
case CommandTraits::KCI_author:
713724
case CommandTraits::KCI_authors:
@@ -732,7 +743,8 @@ void CommentASTToXMLConverter::visitBlockCommandComment(
732743
break;
733744
}
734745

735-
appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
746+
appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind,
747+
ExceptionType);
736748
}
737749

738750
void CommentASTToXMLConverter::visitParamCommandComment(

‎clang/test/Index/comment-to-html-xml-conversion.cpp

+49-30
Original file line numberDiff line numberDiff line change
@@ -1046,82 +1046,101 @@ void comment_to_xml_conversion_todo_4();
10461046
/// Aaa.
10471047
/// \throws Bbb.
10481048
void comment_to_xml_conversion_exceptions_1();
1049-
// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_exceptions_1:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_exceptions_1</Name><USR>c:@F@comment_to_xml_conversion_exceptions_1#</USR><Declaration>void comment_to_xml_conversion_exceptions_1()</Declaration><Abstract><Para> Aaa. </Para></Abstract><Exceptions><Para> Bbb.</Para></Exceptions></Function>]
1049+
// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_exceptions_1:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_exceptions_1</Name><USR>c:@F@comment_to_xml_conversion_exceptions_1#</USR><Declaration>void comment_to_xml_conversion_exceptions_1()</Declaration><Abstract><Para> Aaa. </Para></Abstract><Exceptions><Para>Bbb. </Para></Exceptions></Function>]
10501050
// CHECK-NEXT: CommentAST=[
10511051
// CHECK-NEXT: (CXComment_FullComment
10521052
// CHECK-NEXT: (CXComment_Paragraph
10531053
// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline)
10541054
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
1055-
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throws]
1056-
// CHECK-NEXT: (CXComment_Paragraph
1057-
// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))]
1055+
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throws] Arg[0]=Bbb.
1056+
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace)))]
10581057

10591058
/// Aaa.
10601059
/// \throw Bbb.
10611060
void comment_to_xml_conversion_exceptions_2();
1062-
// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_exceptions_2:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_exceptions_2</Name><USR>c:@F@comment_to_xml_conversion_exceptions_2#</USR><Declaration>void comment_to_xml_conversion_exceptions_2()</Declaration><Abstract><Para> Aaa. </Para></Abstract><Exceptions><Para> Bbb.</Para></Exceptions></Function>]
1061+
// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_exceptions_2:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_exceptions_2</Name><USR>c:@F@comment_to_xml_conversion_exceptions_2#</USR><Declaration>void comment_to_xml_conversion_exceptions_2()</Declaration><Abstract><Para> Aaa. </Para></Abstract><Exceptions><Para>Bbb. </Para></Exceptions></Function>]
10631062
// CHECK-NEXT: CommentAST=[
10641063
// CHECK-NEXT: (CXComment_FullComment
10651064
// CHECK-NEXT: (CXComment_Paragraph
10661065
// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline)
10671066
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
1068-
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throw]
1069-
// CHECK-NEXT: (CXComment_Paragraph
1070-
// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))]
1067+
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throw] Arg[0]=Bbb.
1068+
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace)))]
10711069

10721070
/// Aaa.
10731071
/// \exception Bbb.
10741072
void comment_to_xml_conversion_exceptions_3();
1075-
// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_exceptions_3:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_exceptions_3</Name><USR>c:@F@comment_to_xml_conversion_exceptions_3#</USR><Declaration>void comment_to_xml_conversion_exceptions_3()</Declaration><Abstract><Para> Aaa. </Para></Abstract><Exceptions><Para> Bbb.</Para></Exceptions></Function>]
1073+
// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_exceptions_3:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_exceptions_3</Name><USR>c:@F@comment_to_xml_conversion_exceptions_3#</USR><Declaration>void comment_to_xml_conversion_exceptions_3()</Declaration><Abstract><Para> Aaa. </Para></Abstract><Exceptions><Para>Bbb. </Para></Exceptions></Function>]
10761074
// CHECK-NEXT: CommentAST=[
10771075
// CHECK-NEXT: (CXComment_FullComment
10781076
// CHECK-NEXT: (CXComment_Paragraph
10791077
// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline)
10801078
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
1081-
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[exception]
1082-
// CHECK-NEXT: (CXComment_Paragraph
1083-
// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))]
1079+
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[exception] Arg[0]=Bbb.
1080+
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace)))]
10841081

10851082
/// Aaa.
10861083
/// \throws Bbb.
10871084
/// \throws Ccc.
10881085
/// \throws Ddd.
10891086
void comment_to_xml_conversion_exceptions_4();
1090-
// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_exceptions_4:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_exceptions_4</Name><USR>c:@F@comment_to_xml_conversion_exceptions_4#</USR><Declaration>void comment_to_xml_conversion_exceptions_4()</Declaration><Abstract><Para> Aaa. </Para></Abstract><Exceptions><Para> Bbb. </Para><Para> Ccc. </Para><Para> Ddd.</Para></Exceptions></Function>]
1087+
// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_exceptions_4:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_exceptions_4</Name><USR>c:@F@comment_to_xml_conversion_exceptions_4#</USR><Declaration>void comment_to_xml_conversion_exceptions_4()</Declaration><Abstract><Para> Aaa. </Para></Abstract><Exceptions><Para>Bbb. </Para><Para>Ccc. </Para><Para>Ddd. </Para></Exceptions></Function>]
10911088
// CHECK-NEXT: CommentAST=[
10921089
// CHECK-NEXT: (CXComment_FullComment
10931090
// CHECK-NEXT: (CXComment_Paragraph
10941091
// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline)
10951092
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
1096-
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throws]
1097-
// CHECK-NEXT: (CXComment_Paragraph
1098-
// CHECK-NEXT: (CXComment_Text Text=[ Bbb.] HasTrailingNewline)
1099-
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
1100-
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throws]
1101-
// CHECK-NEXT: (CXComment_Paragraph
1102-
// CHECK-NEXT: (CXComment_Text Text=[ Ccc.] HasTrailingNewline)
1103-
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
1104-
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throws]
1105-
// CHECK-NEXT: (CXComment_Paragraph
1106-
// CHECK-NEXT: (CXComment_Text Text=[ Ddd.]))))]
1093+
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throws] Arg[0]=Bbb.
1094+
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace))
1095+
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throws] Arg[0]=Ccc.
1096+
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace))
1097+
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throws] Arg[0]=Ddd.
1098+
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace)))]
11071099

11081100
/// Aaa.
11091101
/// \throws Bbb.
11101102
/// \throw Ccc.
11111103
void comment_to_xml_conversion_exceptions_5();
1112-
// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_exceptions_5:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_exceptions_5</Name><USR>c:@F@comment_to_xml_conversion_exceptions_5#</USR><Declaration>void comment_to_xml_conversion_exceptions_5()</Declaration><Abstract><Para> Aaa. </Para></Abstract><Exceptions><Para> Bbb. </Para><Para> Ccc.</Para></Exceptions></Function>]
1104+
// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_exceptions_5:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_exceptions_5</Name><USR>c:@F@comment_to_xml_conversion_exceptions_5#</USR><Declaration>void comment_to_xml_conversion_exceptions_5()</Declaration><Abstract><Para> Aaa. </Para></Abstract><Exceptions><Para>Bbb. </Para><Para>Ccc. </Para></Exceptions></Function>]
1105+
// CHECK-NEXT: CommentAST=[
1106+
// CHECK-NEXT: (CXComment_FullComment
1107+
// CHECK-NEXT: (CXComment_Paragraph
1108+
// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline)
1109+
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
1110+
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throws] Arg[0]=Bbb.
1111+
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace))
1112+
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throw] Arg[0]=Ccc.
1113+
// CHECK-NEXT: (CXComment_Paragraph IsWhitespace)))]
1114+
1115+
/// Aaa.
1116+
/// \throws Bbb subsequent arg text
1117+
void comment_to_xml_conversion_exceptions_6();
1118+
// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_exceptions_6:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_exceptions_6</Name><USR>c:@F@comment_to_xml_conversion_exceptions_6#</USR><Declaration>void comment_to_xml_conversion_exceptions_6()</Declaration><Abstract><Para> Aaa. </Para></Abstract><Exceptions><Para>Bbb subsequent arg text</Para></Exceptions></Function>]
11131119
// CHECK-NEXT: CommentAST=[
11141120
// CHECK-NEXT: (CXComment_FullComment
11151121
// CHECK-NEXT: (CXComment_Paragraph
11161122
// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline)
11171123
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
1118-
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throws]
1124+
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throws] Arg[0]=Bbb
11191125
// CHECK-NEXT: (CXComment_Paragraph
1120-
// CHECK-NEXT: (CXComment_Text Text=[ Bbb.] HasTrailingNewline)
1126+
// CHECK-NEXT: (CXComment_Text Text=[subsequent arg text]))))]
1127+
1128+
/// Aaa.
1129+
/// \throws Bbb subsequent arg text
1130+
/// \throw Ccc subsequent arg text
1131+
void comment_to_xml_conversion_exceptions_7();
1132+
// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_exceptions_7:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_exceptions_7</Name><USR>c:@F@comment_to_xml_conversion_exceptions_7#</USR><Declaration>void comment_to_xml_conversion_exceptions_7()</Declaration><Abstract><Para> Aaa. </Para></Abstract><Exceptions><Para>Bbb subsequent arg text </Para><Para>Ccc subsequent arg text</Para></Exceptions></Function>]
1133+
// CHECK-NEXT: CommentAST=[
1134+
// CHECK-NEXT: (CXComment_FullComment
1135+
// CHECK-NEXT: (CXComment_Paragraph
1136+
// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline)
1137+
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
1138+
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throws] Arg[0]=Bbb
1139+
// CHECK-NEXT: (CXComment_Paragraph
1140+
// CHECK-NEXT: (CXComment_Text Text=[subsequent arg text] HasTrailingNewline)
11211141
// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
1122-
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throw]
1142+
// CHECK-NEXT: (CXComment_BlockCommand CommandName=[throw] Arg[0]=Ccc
11231143
// CHECK-NEXT: (CXComment_Paragraph
1124-
// CHECK-NEXT: (CXComment_Text Text=[ Ccc.]))))]
1144+
// CHECK-NEXT: (CXComment_Text Text=[subsequent arg text]))))]
11251145

11261146
#endif
1127-

‎clang/unittests/AST/CommentParser.cpp

+213-1
Original file line numberDiff line numberDiff line change
@@ -1427,8 +1427,220 @@ TEST_F(CommentParserTest, Deprecated) {
14271427
}
14281428
}
14291429

1430+
TEST_F(CommentParserTest, ThrowsCommandHasArg1) {
1431+
const char *Sources[] = {
1432+
"/// @throws int This function throws an integer",
1433+
("/// @throws\n"
1434+
"/// int This function throws an integer"),
1435+
("/// @throws \n"
1436+
"/// int This function throws an integer"),
1437+
("/// @throws\n"
1438+
"/// int\n"
1439+
"/// This function throws an integer"),
1440+
("/// @throws \n"
1441+
"/// int \n"
1442+
"/// This function throws an integer"),
1443+
};
1444+
1445+
for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1446+
FullComment *FC = parseString(Sources[i]);
1447+
ASSERT_TRUE(HasChildCount(FC, 2));
1448+
1449+
ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1450+
{
1451+
BlockCommandComment *BCC;
1452+
ParagraphComment *PC;
1453+
ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1454+
ASSERT_TRUE(HasChildCount(PC, 1));
1455+
ASSERT_TRUE(BCC->getNumArgs() == 1);
1456+
ASSERT_TRUE(BCC->getArgText(0) == "int");
1457+
}
1458+
}
1459+
}
1460+
1461+
TEST_F(CommentParserTest, ThrowsCommandHasArg2) {
1462+
const char *Sources[] = {
1463+
"/// @throws int** This function throws a double pointer to an integer",
1464+
};
1465+
1466+
for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1467+
FullComment *FC = parseString(Sources[i]);
1468+
ASSERT_TRUE(HasChildCount(FC, 2));
1469+
1470+
ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1471+
{
1472+
BlockCommandComment *BCC;
1473+
ParagraphComment *PC;
1474+
ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1475+
ASSERT_TRUE(HasChildCount(PC, 1));
1476+
ASSERT_TRUE(BCC->getNumArgs() == 1);
1477+
ASSERT_TRUE(BCC->getArgText(0) == "int**");
1478+
}
1479+
}
1480+
}
1481+
1482+
TEST_F(CommentParserTest, ThrowsCommandHasArg3) {
1483+
const char *Sources[] = {
1484+
"/// @throws Error<T> error of type Error<T>",
1485+
};
1486+
1487+
for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1488+
FullComment *FC = parseString(Sources[i]);
1489+
ASSERT_TRUE(HasChildCount(FC, 2));
1490+
1491+
ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1492+
{
1493+
BlockCommandComment *BCC;
1494+
ParagraphComment *PC;
1495+
ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1496+
ASSERT_TRUE(HasChildCount(PC, 3)); // Extra children because <T> is parsed
1497+
// as a series of TextComments
1498+
ASSERT_TRUE(BCC->getNumArgs() == 1);
1499+
ASSERT_TRUE(BCC->getArgText(0) == "Error<T>");
1500+
}
1501+
}
1502+
}
1503+
1504+
TEST_F(CommentParserTest, ThrowsCommandHasArg4) {
1505+
const char *Sources[] = {
1506+
"/// @throws Error<Container<T>> nested templates",
1507+
};
1508+
1509+
for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1510+
FullComment *FC = parseString(Sources[i]);
1511+
ASSERT_TRUE(HasChildCount(FC, 2));
1512+
1513+
ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1514+
{
1515+
BlockCommandComment *BCC;
1516+
ParagraphComment *PC;
1517+
ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1518+
ASSERT_TRUE(HasChildCount(PC, 1));
1519+
ASSERT_TRUE(BCC->getNumArgs() == 1);
1520+
ASSERT_TRUE(BCC->getArgText(0) == "Error<Container<T>>");
1521+
}
1522+
}
1523+
}
1524+
1525+
TEST_F(CommentParserTest, ThrowsCommandHasArg5) {
1526+
const char *Sources[] = {
1527+
"/// @throws Error<Ts...> variadic templates",
1528+
};
1529+
1530+
for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1531+
FullComment *FC = parseString(Sources[i]);
1532+
ASSERT_TRUE(HasChildCount(FC, 2));
1533+
1534+
ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1535+
{
1536+
BlockCommandComment *BCC;
1537+
ParagraphComment *PC;
1538+
ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1539+
ASSERT_TRUE(HasChildCount(PC, 1));
1540+
ASSERT_TRUE(BCC->getNumArgs() == 1);
1541+
ASSERT_TRUE(BCC->getArgText(0) == "Error<Ts...>");
1542+
}
1543+
}
1544+
}
1545+
1546+
TEST_F(CommentParserTest, ThrowsCommandHasArg6) {
1547+
const char *Sources[] = {
1548+
"/// @throws Foo<(1 > 0)> typo1",
1549+
};
1550+
1551+
for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1552+
FullComment *FC = parseString(Sources[i]);
1553+
ASSERT_TRUE(HasChildCount(FC, 2));
1554+
1555+
ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1556+
{
1557+
BlockCommandComment *BCC;
1558+
ParagraphComment *PC;
1559+
ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1560+
ASSERT_TRUE(HasChildCount(PC, 1));
1561+
ASSERT_TRUE(BCC->getNumArgs() == 1);
1562+
ASSERT_TRUE(BCC->getArgText(0) == "Foo<(1 >");
1563+
}
1564+
}
1565+
}
1566+
1567+
// No matter the number of (unmatched) opening brackets, no type is parsed.
1568+
TEST_F(CommentParserTest, ThrowsCommandHasArg7) {
1569+
const char *Sources[] = {
1570+
"/// @throws Foo<",
1571+
"/// @throws Foo<<<",
1572+
"/// @throws Foo<<<<<<<",
1573+
"/// @throws Foo<\n",
1574+
"/// @throws Foo<<<\n",
1575+
"/// @throws Foo<<<<<<<\n",
1576+
};
1577+
1578+
for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1579+
FullComment *FC = parseString(Sources[i]);
1580+
ASSERT_TRUE(HasChildCount(FC, 2));
1581+
1582+
ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1583+
{
1584+
BlockCommandComment *BCC;
1585+
ParagraphComment *PC;
1586+
ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1587+
ASSERT_TRUE(HasChildCount(PC, 0));
1588+
ASSERT_TRUE(BCC->getNumArgs() == 0);
1589+
}
1590+
}
1591+
}
1592+
1593+
// Types with a non-matching closing bracket are parsed as if they are a type
1594+
TEST_F(CommentParserTest, ThrowsCommandHasArg8) {
1595+
const char *Sources[] = {
1596+
"/// @throws Foo>",
1597+
"/// @throws Foo>\n",
1598+
};
1599+
1600+
for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1601+
FullComment *FC = parseString(Sources[i]);
1602+
ASSERT_TRUE(HasChildCount(FC, 2));
1603+
1604+
ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1605+
{
1606+
BlockCommandComment *BCC;
1607+
ParagraphComment *PC;
1608+
ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1609+
ASSERT_TRUE(HasChildCount(PC, 0));
1610+
ASSERT_TRUE(BCC->getNumArgs() == 1);
1611+
ASSERT_TRUE(BCC->getArgText(0) == "Foo>");
1612+
}
1613+
}
1614+
}
1615+
1616+
// Everying up until the end of the paragraph comment will be
1617+
// eaten up if the template sequence is unterminated (i.e. number of
1618+
// opening and closing brackets is not equal).
1619+
TEST_F(CommentParserTest, ThrowsCommandHasArg9) {
1620+
const char *Sources[] = {
1621+
"/// @throws Foo<Bar<t>\n"
1622+
"/// Aaa\n"
1623+
"///\n"
1624+
"/// Bbb\n"
1625+
};
1626+
1627+
for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1628+
FullComment *FC = parseString(Sources[i]);
1629+
ASSERT_TRUE(HasChildCount(FC, 3));
1630+
1631+
ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1632+
{
1633+
BlockCommandComment *BCC;
1634+
ParagraphComment *PC;
1635+
ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1636+
ASSERT_TRUE(HasChildCount(PC, 0));
1637+
ASSERT_TRUE(BCC->getNumArgs() == 0);
1638+
}
1639+
}
1640+
}
1641+
1642+
14301643
} // unnamed namespace
14311644

14321645
} // end namespace comments
14331646
} // end namespace clang
1434-

0 commit comments

Comments
 (0)
Please sign in to comment.