diff --git a/clang/include/clang/AST/Comment.h b/clang/include/clang/AST/Comment.h --- a/clang/include/clang/AST/Comment.h +++ b/clang/include/clang/AST/Comment.h @@ -194,6 +194,11 @@ #include "clang/AST/CommentNodes.inc" }; + struct Argument { + SourceRange Range; + StringRef Text; + }; + Comment(CommentKind K, SourceLocation LocBegin, SourceLocation LocEnd) : @@ -296,13 +301,6 @@ /// A command with word-like arguments that is considered inline content. class InlineCommandComment : public InlineContentComment { public: - struct Argument { - SourceRange Range; - StringRef Text; - - Argument(SourceRange Range, StringRef Text) : Range(Range), Text(Text) { } - }; - /// The most appropriate rendering mode for this command, chosen on command /// semantics in Doxygen. enum RenderKind { @@ -588,15 +586,6 @@ /// arguments depends on command name) and a paragraph as an argument /// (e. g., \\brief). class BlockCommandComment : public BlockContentComment { -public: - struct Argument { - SourceRange Range; - StringRef Text; - - Argument() { } - Argument(SourceRange Range, StringRef Text) : Range(Range), Text(Text) { } - }; - protected: /// Word-like arguments. ArrayRef Args; diff --git a/clang/include/clang/AST/CommentCommands.td b/clang/include/clang/AST/CommentCommands.td --- a/clang/include/clang/AST/CommentCommands.td +++ b/clang/include/clang/AST/CommentCommands.td @@ -31,6 +31,7 @@ } class InlineCommand : Command { + let NumArgs = 1; let IsInlineCommand = 1; } @@ -86,6 +87,7 @@ def P : InlineCommand<"p">; def A : InlineCommand<"a">; def E : InlineCommand<"e">; +def N : InlineCommand<"n"> { let NumArgs = 0; } def Em : InlineCommand<"em">; def Emoji : InlineCommand<"emoji">; diff --git a/clang/include/clang/AST/CommentParser.h b/clang/include/clang/AST/CommentParser.h --- a/clang/include/clang/AST/CommentParser.h +++ b/clang/include/clang/AST/CommentParser.h @@ -97,9 +97,8 @@ void parseTParamCommandArgs(TParamCommandComment *TPC, TextTokenRetokenizer &Retokenizer); - void parseBlockCommandArgs(BlockCommandComment *BC, - TextTokenRetokenizer &Retokenizer, - unsigned NumArgs); + ArrayRef + parseCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs); BlockCommandComment *parseBlockCommand(); InlineCommandComment *parseInlineCommand(); diff --git a/clang/include/clang/AST/CommentSema.h b/clang/include/clang/AST/CommentSema.h --- a/clang/include/clang/AST/CommentSema.h +++ b/clang/include/clang/AST/CommentSema.h @@ -128,16 +128,10 @@ void actOnTParamCommandFinish(TParamCommandComment *Command, ParagraphComment *Paragraph); - InlineCommandComment *actOnInlineCommand(SourceLocation CommandLocBegin, - SourceLocation CommandLocEnd, - unsigned CommandID); - InlineCommandComment *actOnInlineCommand(SourceLocation CommandLocBegin, SourceLocation CommandLocEnd, unsigned CommandID, - SourceLocation ArgLocBegin, - SourceLocation ArgLocEnd, - StringRef Arg); + ArrayRef Args); InlineContentComment *actOnUnknownCommand(SourceLocation LocBegin, SourceLocation LocEnd, diff --git a/clang/include/clang/Basic/DiagnosticCommentKinds.td b/clang/include/clang/Basic/DiagnosticCommentKinds.td --- a/clang/include/clang/Basic/DiagnosticCommentKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommentKinds.td @@ -155,8 +155,8 @@ // inline contents commands -def warn_doc_inline_contents_no_argument : Warning< - "'%select{\\|@}0%1' command does not have a valid word argument">, +def warn_doc_inline_command_not_enough_arguments : Warning< + "'%select{\\|@}0%1' command has %plural{0:no|:%2}2 word argument%s2, expected %3">, InGroup, DefaultIgnore; // verbatim block commands diff --git a/clang/lib/AST/CommentParser.cpp b/clang/lib/AST/CommentParser.cpp --- a/clang/lib/AST/CommentParser.cpp +++ b/clang/lib/AST/CommentParser.cpp @@ -289,22 +289,19 @@ Arg.getText()); } -void Parser::parseBlockCommandArgs(BlockCommandComment *BC, - TextTokenRetokenizer &Retokenizer, - unsigned NumArgs) { - typedef BlockCommandComment::Argument Argument; - Argument *Args = - new (Allocator.Allocate(NumArgs)) Argument[NumArgs]; +ArrayRef +Parser::parseCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs) { + auto *Args = new (Allocator.Allocate(NumArgs)) + Comment::Argument[NumArgs]; unsigned ParsedArgs = 0; Token Arg; while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) { - Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(), - Arg.getEndLocation()), - Arg.getText()); + Args[ParsedArgs] = Comment::Argument{ + SourceRange(Arg.getLocation(), Arg.getEndLocation()), Arg.getText()}; ParsedArgs++; } - S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs)); + return llvm::makeArrayRef(Args, ParsedArgs); } BlockCommandComment *Parser::parseBlockCommand() { @@ -360,7 +357,7 @@ else if (TPC) parseTParamCommandArgs(TPC, Retokenizer); else - parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs); + S.actOnBlockCommandArgs(BC, parseCommandArgs(Retokenizer, Info->NumArgs)); Retokenizer.putBackLeftoverTokens(); } @@ -401,32 +398,24 @@ InlineCommandComment *Parser::parseInlineCommand() { assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command)); + const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID()); const Token CommandTok = Tok; consumeToken(); TextTokenRetokenizer Retokenizer(Allocator, *this); + ArrayRef Args = + parseCommandArgs(Retokenizer, Info->NumArgs); - Token ArgTok; - bool ArgTokValid = Retokenizer.lexWord(ArgTok); - - InlineCommandComment *IC; - if (ArgTokValid) { - IC = S.actOnInlineCommand(CommandTok.getLocation(), - CommandTok.getEndLocation(), - CommandTok.getCommandID(), - ArgTok.getLocation(), - ArgTok.getEndLocation(), - ArgTok.getText()); - } else { - IC = S.actOnInlineCommand(CommandTok.getLocation(), - CommandTok.getEndLocation(), - CommandTok.getCommandID()); + InlineCommandComment *IC = S.actOnInlineCommand( + CommandTok.getLocation(), CommandTok.getEndLocation(), + CommandTok.getCommandID(), Args); + if (Args.size() < Info->NumArgs) { Diag(CommandTok.getEndLocation().getLocWithOffset(1), - diag::warn_doc_inline_contents_no_argument) - << CommandTok.is(tok::at_command) - << Traits.getCommandInfo(CommandTok.getCommandID())->Name + diag::warn_doc_inline_command_not_enough_arguments) + << CommandTok.is(tok::at_command) << Info->Name << Args.size() + << Info->NumArgs << SourceRange(CommandTok.getLocation(), CommandTok.getEndLocation()); } diff --git a/clang/lib/AST/CommentSema.cpp b/clang/lib/AST/CommentSema.cpp --- a/clang/lib/AST/CommentSema.cpp +++ b/clang/lib/AST/CommentSema.cpp @@ -265,10 +265,8 @@ // User didn't provide a direction argument. Command->setDirection(ParamCommandComment::In, /* Explicit = */ false); } - typedef BlockCommandComment::Argument Argument; - Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin, - ArgLocEnd), - Arg); + auto *A = new (Allocator) + Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg}; Command->setArgs(llvm::makeArrayRef(A, 1)); } @@ -303,10 +301,8 @@ // 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); + auto *A = new (Allocator) + Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg}; Command->setArgs(llvm::makeArrayRef(A, 1)); if (!isTemplateOrSpecialization()) { @@ -361,37 +357,15 @@ checkBlockCommandEmptyParagraph(Command); } -InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin, - SourceLocation CommandLocEnd, - unsigned CommandID) { - ArrayRef Args; +InlineCommandComment * +Sema::actOnInlineCommand(SourceLocation CommandLocBegin, + SourceLocation CommandLocEnd, unsigned CommandID, + ArrayRef Args) { StringRef CommandName = Traits.getCommandInfo(CommandID)->Name; - return new (Allocator) InlineCommandComment( - CommandLocBegin, - CommandLocEnd, - CommandID, - getInlineCommandRenderKind(CommandName), - Args); -} -InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin, - SourceLocation CommandLocEnd, - unsigned CommandID, - SourceLocation ArgLocBegin, - SourceLocation ArgLocEnd, - StringRef Arg) { - typedef InlineCommandComment::Argument Argument; - Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin, - ArgLocEnd), - Arg); - StringRef CommandName = Traits.getCommandInfo(CommandID)->Name; - - return new (Allocator) InlineCommandComment( - CommandLocBegin, - CommandLocEnd, - CommandID, - getInlineCommandRenderKind(CommandName), - llvm::makeArrayRef(A, 1)); + return new (Allocator) + InlineCommandComment(CommandLocBegin, CommandLocEnd, CommandID, + getInlineCommandRenderKind(CommandName), Args); } InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin, diff --git a/clang/test/AST/ast-dump-comment.cpp b/clang/test/AST/ast-dump-comment.cpp --- a/clang/test/AST/ast-dump-comment.cpp +++ b/clang/test/AST/ast-dump-comment.cpp @@ -62,6 +62,12 @@ // CHECK: VarDecl{{.*}}Test_InlineCommandComment // CHECK: InlineCommandComment{{.*}} Name="c" RenderMonospaced Arg[0]="Aaa" +/// \n Aaa +int Test_InlineCommandComment_NoArgs; +// CHECK: VarDecl{{.*}}Test_InlineCommandComment_NoArgs +// CHECK: InlineCommandComment{{.*}} Name="n" RenderNormal +// CHECK-NEXT: TextComment{{.*}} Text=" Aaa" + /// \anchor Aaa int Test_InlineCommandCommentAnchor; // CHECK: VarDecl{{.*}}Test_InlineCommandComment diff --git a/clang/test/Headers/x86-intrinsics-headers-clean.cpp b/clang/test/Headers/x86-intrinsics-headers-clean.cpp --- a/clang/test/Headers/x86-intrinsics-headers-clean.cpp +++ b/clang/test/Headers/x86-intrinsics-headers-clean.cpp @@ -1,11 +1,11 @@ // Make sure the intrinsic headers compile cleanly with no warnings or errors. // RUN: %clang_cc1 -ffreestanding -triple i386-unknown-unknown \ -// RUN: -Wextra -Werror -Wsystem-headers -Wsign-conversion -Wcast-qual -Wdocumentation \ +// RUN: -Wextra -Werror -Wsystem-headers -Wsign-conversion -Wcast-qual -Wdocumentation -Wdocumentation-pedantic -Wdocumentation-unknown-command \ // RUN: -fsyntax-only -fretain-comments-from-system-headers -flax-vector-conversions=none -x c++ -verify %s // RUN: %clang_cc1 -ffreestanding -triple x86_64-unknown-unknown \ -// RUN: -Wextra -Werror -Wsystem-headers -Wsign-conversion -Wcast-qual -Wdocumentation \ +// RUN: -Wextra -Werror -Wsystem-headers -Wsign-conversion -Wcast-qual -Wdocumentation -Wdocumentation-pedantic -Wdocumentation-unknown-command \ // RUN: -fsyntax-only -fretain-comments-from-system-headers -flax-vector-conversions=none -x c++ -verify %s // expected-no-diagnostics diff --git a/clang/test/Sema/warn-documentation.cpp b/clang/test/Sema/warn-documentation.cpp --- a/clang/test/Sema/warn-documentation.cpp +++ b/clang/test/Sema/warn-documentation.cpp @@ -1116,49 +1116,49 @@ void test_attach38::test_attach39(int, B); // The inline comments expect a string after the command. -// expected-warning@+1 {{'\a' command does not have a valid word argument}} +// expected-warning@+1 {{'\a' command has no word arguments, expected 1}} /// \a int test_inline_no_argument_a_bad(int); /// \a A int test_inline_no_argument_a_good(int); -// expected-warning@+1 {{'\anchor' command does not have a valid word argument}} +// expected-warning@+1 {{'\anchor' command has no word arguments, expected 1}} /// \anchor int test_inline_no_argument_anchor_bad(int); /// \anchor A int test_inline_no_argument_anchor_good(int); -// expected-warning@+1 {{'@b' command does not have a valid word argument}} +// expected-warning@+1 {{'@b' command has no word arguments, expected 1}} /// @b int test_inline_no_argument_b_bad(int); /// @b A int test_inline_no_argument_b_good(int); -// expected-warning@+1 {{'\c' command does not have a valid word argument}} +// expected-warning@+1 {{'\c' command has no word arguments, expected 1}} /// \c int test_inline_no_argument_c_bad(int); /// \c A int test_inline_no_argument_c_good(int); -// expected-warning@+1 {{'\e' command does not have a valid word argument}} +// expected-warning@+1 {{'\e' command has no word arguments, expected 1}} /// \e int test_inline_no_argument_e_bad(int); /// \e A int test_inline_no_argument_e_good(int); -// expected-warning@+1 {{'\em' command does not have a valid word argument}} +// expected-warning@+1 {{'\em' command has no word arguments, expected 1}} /// \em int test_inline_no_argument_em_bad(int); /// \em A int test_inline_no_argument_em_good(int); -// expected-warning@+1 {{'\p' command does not have a valid word argument}} +// expected-warning@+1 {{'\p' command has no word arguments, expected 1}} /// \p int test_inline_no_argument_p_bad(int);