Skip to content

Commit 52c558f

Browse files
authored
Fix release scripts and pocodoc (pocoproject#5194)
* fix(release): modified mkrelease to use CMake insteadk of make * fix(release): release scripts fail on error. * fix(CppParser): incorrect handling of "default" keyword caused parser to crash at dereferencing null pointer. * fix(CppParser): Fix hangs while parsing, parse anonymous namespaces and improved parsing of string literals. * fix(CppParser): add unit tests and address PR review comments
1 parent 6189cfa commit 52c558f

File tree

12 files changed

+482
-324
lines changed

12 files changed

+482
-324
lines changed

.github/workflows/packages-qa.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@ jobs:
1616
uses: actions/checkout@v2
1717
-
1818
name: Install packages
19-
run: sudo apt -y update && sudo apt -y install libssl-dev unixodbc-dev redis-server libmysqlclient-dev libpq-dev
19+
run: sudo apt -y update && sudo apt -y install cmake ninja-build libssl-dev unixodbc-dev redis-server libmysqlclient-dev libpq-dev
2020
-
2121
name: Build documentation
2222
run: |
2323
export POCO_BASE=`pwd`
2424
export PATH=$POCO_BASE/release/script:$PATH
25-
export LD_LIBRARY_PATH=$POCO_BASE/stage/tools/lib/Linux/x86_64
2625
mkdoc all
2726
2827
mkrelease_win:

.github/workflows/publish_release.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
uses: actions/checkout@v2
2121
-
2222
name: Install packages
23-
run: sudo apt -y update && sudo apt -y install libssl-dev unixodbc-dev redis-server libmysqlclient-dev libpq-dev
23+
run: sudo apt -y update && sudo apt -y install cmake ninja-build libssl-dev unixodbc-dev redis-server libmysqlclient-dev libpq-dev
2424
-
2525
name: Install SSH key
2626
run: |
@@ -33,7 +33,6 @@ jobs:
3333
run: |
3434
export POCO_BASE=`pwd`
3535
export PATH=$POCO_BASE/release/script:$PATH
36-
export LD_LIBRARY_PATH=$POCO_BASE/stage/tools/lib/Linux/x86_64
3736
mkdoc all
3837
-
3938
name: Copy to web server

CppParser/include/Poco/CppParser/CppToken.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ class CppParser_API IdentifierToken: public CppToken
209209
int asInteger() const;
210210

211211
private:
212+
void finishRawString(std::istream& istr);
213+
212214
using KWMap = std::map<std::string, int>;
213215

214216
KWMap _kwMap;

CppParser/include/Poco/CppParser/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,10 @@ class CppParser_API Parser
8181
const Poco::Token* parseBaseClassList(const Poco::Token* pNext, Struct* pClass);
8282
const Poco::Token* parseClassMembers(const Poco::Token* pNext, Struct* pClass);
8383
const Poco::Token* parseAccess(const Poco::Token* pNext);
84+
const Poco::Token* parseStaticAssert(const Poco::Token* pNext);
8485
const Poco::Token* parseIdentifier(const Poco::Token* pNext, std::string& id);
8586
const Poco::Token* parseAttributes(const Poco::Token* pNext, std::string& attrs);
87+
const Poco::Token* parseParenthesized(const Poco::Token* pNext, std::string& decl);
8688

8789
void addSymbol(Symbol* pSymbol, int lineNumber, bool addGST = true);
8890
void pushNameSpace(NameSpace* pNameSpace, int lineNumber, bool addGST = true);

CppParser/src/CppToken.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,54 @@ void IdentifierToken::finish(std::istream& istr)
369369
_value += (char) istr.get();
370370
next = istr.peek();
371371
}
372+
373+
// C++11 raw string literals: R"delim(...)delim", also u8R"...", uR"...", UR"...", LR"..."
374+
// Without this handling, the tokenizer would split R"(")" into separate tokens:
375+
// identifier 'R', string literal '("', operator ')', and an unterminated '"',
376+
// causing "Unterminated character literal" errors downstream.
377+
if ((_value == "R" || _value == "u8R" || _value == "uR" || _value == "UR" || _value == "LR")
378+
&& istr.peek() == '"')
379+
{
380+
finishRawString(istr);
381+
}
382+
}
383+
384+
385+
void IdentifierToken::finishRawString(std::istream& istr)
386+
{
387+
// Consume the entire raw string literal as part of this identifier token
388+
// so that parseBlock() and other consumers can safely skip over it.
389+
// The delimiter between " and ( can be any character sequence,
390+
// e.g. R"---(content)---" has delimiter "---".
391+
_value += (char) istr.get(); // consume opening "
392+
393+
// Read optional delimiter characters until '('
394+
std::string delimiter;
395+
int next = istr.peek();
396+
while (next != -1 && next != '(' && next != '\n')
397+
{
398+
delimiter += (char) istr.get();
399+
_value += delimiter.back();
400+
next = istr.peek();
401+
}
402+
if (next == '(')
403+
{
404+
_value += (char) istr.get(); // consume '('
405+
// Scan for closing sequence: )delimiter"
406+
// Use a sliding window to match the end pattern correctly.
407+
std::string endPattern = ")" + delimiter + "\"";
408+
std::string window;
409+
while (!istr.eof())
410+
{
411+
char c = (char) istr.get();
412+
_value += c;
413+
window += c;
414+
if (window.size() > endPattern.size())
415+
window.erase(0, 1);
416+
if (window == endPattern)
417+
break;
418+
}
419+
}
372420
}
373421

374422

@@ -420,6 +468,23 @@ void StringLiteralToken::finish(std::istream& istr)
420468
_value += (char) next;
421469
}
422470
else throw SyntaxException("Unterminated string literal");
471+
472+
// C++14 string literal suffixes: "str"s (std::string), "str"sv (std::string_view),
473+
// and user-defined literal suffixes starting with _ (e.g. "str"_mylit).
474+
// Consume the suffix as part of this token so the parser doesn't see a
475+
// stray identifier immediately following a string literal.
476+
next = istr.peek();
477+
if (next == 's' || next == '_')
478+
{
479+
while ((next >= 'A' && next <= 'Z') ||
480+
(next >= 'a' && next <= 'z') ||
481+
(next >= '0' && next <= '9') ||
482+
next == '_')
483+
{
484+
_value += (char) istr.get();
485+
next = istr.peek();
486+
}
487+
}
423488
}
424489

425490

CppParser/src/Parser.cpp

Lines changed: 102 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ namespace Poco {
4646
namespace CppParser {
4747

4848

49+
// Context-sensitive identifiers (not reserved keywords in C++,
50+
// but have special meaning in certain contexts)
51+
static const std::string ID_OVERRIDE("override");
52+
static const std::string ID_FINAL("final");
53+
54+
4955
Parser::Parser(NameSpace::SymbolTable& gst, const std::string& file, std::istream& istr):
5056
_gst(gst),
5157
_istr(istr),
@@ -198,6 +204,9 @@ const Token* Parser::parseFile(const Token* pNext)
198204
case IdentifierToken::KW_ENUM:
199205
pNext = parseEnum(pNext);
200206
break;
207+
case IdentifierToken::KW_STATIC_ASSERT:
208+
pNext = parseStaticAssert(pNext);
209+
break;
201210
default:
202211
pNext = parseVarFunc(pNext);
203212
}
@@ -263,13 +272,23 @@ const Token* Parser::parseNameSpace(const Token* pNext)
263272
case IdentifierToken::KW_ENUM:
264273
pNext = parseEnum(pNext);
265274
break;
275+
case IdentifierToken::KW_STATIC_ASSERT:
276+
pNext = parseStaticAssert(pNext);
277+
break;
266278
default:
267279
pNext = parseVarFunc(pNext);
268280
}
269281
}
270282
expectOperator(pNext, OperatorToken::OP_CLOSBRACE, "}");
271283
pNext = next();
272284
}
285+
else if (isOperator(pNext, OperatorToken::OP_OPENBRACE))
286+
{
287+
// Anonymous namespace (namespace { ... }) contains implementation
288+
// details not relevant for API documentation; skip the entire block.
289+
pNext = parseBlock(pNext);
290+
return pNext;
291+
}
273292
else syntaxError("namespace name");
274293
popNameSpace();
275294
return pNext;
@@ -305,7 +324,7 @@ const Token* Parser::parseClass(const Token* pNext, std::string& decl)
305324
pNext = next();
306325

307326
bool isFinal = false;
308-
if (isIdentifier(pNext) && pNext->asString() == "final")
327+
if (isIdentifier(pNext) && pNext->asString() == ID_FINAL)
309328
{
310329
pNext = next();
311330
isFinal = true;
@@ -424,6 +443,9 @@ const Token* Parser::parseClassMembers(const Token* pNext, Struct* /*pClass*/)
424443
case IdentifierToken::KW_FRIEND:
425444
pNext = parseFriend(pNext);
426445
break;
446+
case IdentifierToken::KW_STATIC_ASSERT:
447+
pNext = parseStaticAssert(pNext);
448+
break;
427449
case OperatorToken::OP_COMPL:
428450
default:
429451
pNext = parseVarFunc(pNext);
@@ -569,12 +591,19 @@ const Token* Parser::parseFriend(const Token* pNext)
569591
{
570592
poco_assert (isKeyword(pNext, IdentifierToken::KW_FRIEND));
571593

594+
// Friend declarations come in two forms:
595+
// friend class Foo; — ends with ;
596+
// friend bool operator==(A a, B b) { ... } — inline definition ends with }
597+
// Skip the entire declaration in both cases.
572598
pNext = next();
573-
574-
while (!isOperator(pNext, OperatorToken::OP_SEMICOLON) && !isEOF(pNext))
599+
while (!isOperator(pNext, OperatorToken::OP_SEMICOLON) &&
600+
!isOperator(pNext, OperatorToken::OP_OPENBRACE) &&
601+
!isEOF(pNext))
575602
pNext = next();
576603

577-
if (isOperator(pNext, OperatorToken::OP_SEMICOLON))
604+
if (isOperator(pNext, OperatorToken::OP_OPENBRACE))
605+
pNext = parseBlock(pNext);
606+
else if (isOperator(pNext, OperatorToken::OP_SEMICOLON))
578607
pNext = next();
579608
return pNext;
580609
}
@@ -601,6 +630,18 @@ const Token* Parser::parseVarFunc(const Token* pNext, std::string& decl)
601630
}
602631
else
603632
{
633+
while (isKeyword(pNext, IdentifierToken::KW_ALIGNAS) ||
634+
isKeyword(pNext, IdentifierToken::KW_DECLTYPE))
635+
{
636+
// These keywords take parenthesized arguments and are part
637+
// of the declaration, not function names
638+
append(decl, pNext);
639+
pNext = next();
640+
if (isOperator(pNext, OperatorToken::OP_OPENPARENT))
641+
{
642+
pNext = parseParenthesized(pNext, decl);
643+
}
644+
}
604645
append(decl, pNext);
605646
pNext = next();
606647
bool isOperatorKeyword = false;
@@ -691,6 +732,7 @@ const Token* Parser::parseFunc(const Token* pNext, const std::string& attrs, std
691732
{
692733
if (pFunc) pFunc->makeConst();
693734
pNext = next();
735+
continue;
694736
}
695737
if (isKeyword(pNext, IdentifierToken::KW_THROW))
696738
{
@@ -702,13 +744,18 @@ const Token* Parser::parseFunc(const Token* pNext, const std::string& attrs, std
702744
{
703745
if (pFunc) pFunc->makeNoexcept();
704746
pNext = next();
747+
if (isOperator(pNext, OperatorToken::OP_OPENPARENT))
748+
{
749+
std::string tmp;
750+
pNext = parseParenthesized(pNext, tmp);
751+
}
705752
}
706-
else if (isIdentifier(pNext) && pNext->asString() == "override")
753+
else if (isIdentifier(pNext) && pNext->asString() == ID_OVERRIDE)
707754
{
708755
if (pFunc) pFunc->makeOverride();
709756
pNext = next();
710757
}
711-
else if (isIdentifier(pNext) && pNext->asString() == "final")
758+
else if (isIdentifier(pNext) && pNext->asString() == ID_FINAL)
712759
{
713760
if (pFunc) pFunc->makeFinal();
714761
pNext = next();
@@ -717,18 +764,26 @@ const Token* Parser::parseFunc(const Token* pNext, const std::string& attrs, std
717764
{
718765
break; // handled below
719766
}
767+
else break;
720768
}
721769
if (isOperator(pNext, OperatorToken::OP_ASSIGN))
722770
{
723771
pNext = next();
724772
if (!pNext->is(Token::INTEGER_LITERAL_TOKEN) && !isKeyword(pNext, IdentifierToken::KW_DEFAULT) && !isKeyword(pNext, IdentifierToken::KW_DELETE))
725773
syntaxError("0, default or delete");
726774
if (isKeyword(pNext, IdentifierToken::KW_DEFAULT))
727-
pFunc->makeDefault();
775+
{
776+
if (pFunc) pFunc->makeDefault();
777+
}
728778
else if (isKeyword(pNext, IdentifierToken::KW_DELETE))
729-
pFunc->makeDelete();
779+
{
780+
if (pFunc) pFunc->makeDelete();
781+
}
782+
else
783+
{
784+
if (pFunc) pFunc->makePureVirtual();
785+
}
730786
pNext = next();
731-
if (pFunc) pFunc->makePureVirtual();
732787
expectOperator(pNext, OperatorToken::OP_SEMICOLON, ";");
733788
}
734789
else if (isOperator(pNext, OperatorToken::OP_OPENBRACE) || isOperator(pNext, OperatorToken::OP_COLON))
@@ -957,6 +1012,44 @@ const Poco::Token* Parser::parseAttributes(const Poco::Token* pNext, std::string
9571012
}
9581013

9591014

1015+
const Token* Parser::parseParenthesized(const Token* pNext, std::string& decl)
1016+
{
1017+
poco_assert (isOperator(pNext, OperatorToken::OP_OPENPARENT));
1018+
1019+
append(decl, pNext);
1020+
pNext = next();
1021+
int depth = 1;
1022+
while (depth > 0 && !isEOF(pNext))
1023+
{
1024+
if (isOperator(pNext, OperatorToken::OP_OPENPARENT))
1025+
++depth;
1026+
else if (isOperator(pNext, OperatorToken::OP_CLOSPARENT))
1027+
--depth;
1028+
if (depth > 0)
1029+
{
1030+
append(decl, pNext);
1031+
pNext = next();
1032+
}
1033+
}
1034+
append(decl, pNext); // closing ')'
1035+
pNext = next();
1036+
return pNext;
1037+
}
1038+
1039+
1040+
const Token* Parser::parseStaticAssert(const Token* pNext)
1041+
{
1042+
poco_assert (isKeyword(pNext, IdentifierToken::KW_STATIC_ASSERT));
1043+
1044+
// skip static_assert(...); — not a declaration
1045+
while (!isOperator(pNext, OperatorToken::OP_SEMICOLON) && !isEOF(pNext))
1046+
pNext = next();
1047+
if (isOperator(pNext, OperatorToken::OP_SEMICOLON))
1048+
pNext = next();
1049+
return pNext;
1050+
}
1051+
1052+
9601053
void Parser::addSymbol(Symbol* pSymbol, int lineNumber, bool addGST)
9611054
{
9621055
pSymbol->setLineNumber(lineNumber);

0 commit comments

Comments
 (0)