diff --git a/clang/include/clang/AST/CommentLexer.h b/clang/include/clang/AST/CommentLexer.h --- a/clang/include/clang/AST/CommentLexer.h +++ b/clang/include/clang/AST/CommentLexer.h @@ -320,6 +320,9 @@ /// Eat string matching regexp \code \s*\* \endcode. void skipLineStartingDecorations(); + /// Skip over pure text. + const char *skipTextToken(); + /// Lex comment text, including commands if ParseCommands is set to true. void lexCommentText(Token &T); diff --git a/clang/lib/AST/CommentLexer.cpp b/clang/lib/AST/CommentLexer.cpp --- a/clang/lib/AST/CommentLexer.cpp +++ b/clang/lib/AST/CommentLexer.cpp @@ -270,6 +270,29 @@ BufferPtr = TokEnd; } +const char *Lexer::skipTextToken() { + const char *TokenPtr = BufferPtr; + assert(TokenPtr < CommentEnd); + StringRef TokStartSymbols = ParseCommands ? "\n\r\\@\"&<" : "\n\r"; + +again: + size_t End = + StringRef(TokenPtr, CommentEnd - TokenPtr).find_first_of(TokStartSymbols); + if (End == StringRef::npos) + return CommentEnd; + + // Doxygen doesn't recognize any commands in a one-line double quotation. + // If we don't find an ending quotation mark, we pretend it never began. + if (*(TokenPtr + End) == '\"') { + TokenPtr += End + 1; + End = StringRef(TokenPtr, CommentEnd - TokenPtr).find_first_of("\n\r\""); + if (End != StringRef::npos && *(TokenPtr + End) == '\"') + TokenPtr += End + 1; + goto again; + } + return TokenPtr + End; +} + void Lexer::lexCommentText(Token &T) { assert(CommentState == LCS_InsideBCPLComment || CommentState == LCS_InsideCComment); @@ -290,17 +313,8 @@ skipLineStartingDecorations(); return; - default: { - StringRef TokStartSymbols = ParseCommands ? "\n\r\\@&<" : "\n\r"; - size_t End = StringRef(TokenPtr, CommentEnd - TokenPtr) - .find_first_of(TokStartSymbols); - if (End != StringRef::npos) - TokenPtr += End; - else - TokenPtr = CommentEnd; - formTextToken(T, TokenPtr); - return; - } + default: + return formTextToken(T, skipTextToken()); } }; diff --git a/clang/test/Sema/warn-documentation-unknown-command.cpp b/clang/test/Sema/warn-documentation-unknown-command.cpp --- a/clang/test/Sema/warn-documentation-unknown-command.cpp +++ b/clang/test/Sema/warn-documentation-unknown-command.cpp @@ -9,6 +9,15 @@ /// \retur aaa int test_unknown_comand_2(); +/// We don't recognize commands in double quotes: "\n\t @unknown2". +int test_unknown_comand_3(); + +// expected-warning@+2 {{unknown command tag name}} +// expected-warning@+2 {{unknown command tag name}} +/// But it has to be a single line: "\unknown3 +/// @unknown4" (Doxygen treats multi-line quotes inconsistently.) +int test_unknown_comand_4(); + // RUN: c-index-test -test-load-source all -Wdocumentation-unknown-command %s > /dev/null 2> %t.err // RUN: FileCheck < %t.err -check-prefix=CHECK-RANGE %s // CHECK-RANGE: warn-documentation-unknown-command.cpp:5:9:{5:9-5:17}: warning: unknown command tag name 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 @@ -125,6 +125,16 @@ /// \brief \c Aaa int test_block_command6(int); +// We don't recognize comments in double quotes. +/// "\brief \returns Aaa" +int test_block_command7(int); + +// But only if they're single-line. (Doxygen treats multi-line quotes inconsistently.) +// expected-warning@+1 {{empty paragraph passed to '\brief' command}} +/// "\brief +/// \returns Aaa" +int test_block_command8(int); + // expected-warning@+5 {{duplicated command '\brief'}} expected-note@+1 {{previous command '\brief' here}} /// \brief Aaa ///