Skip to content

Commit d5951fa

Browse files
authored
Tokenizer: Add attribute for exported symbols (cppcheck-opensource#5043)
1 parent d24a134 commit d5951fa

4 files changed

Lines changed: 141 additions & 88 deletions

File tree

lib/token.h

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,12 @@ class CPPCHECKLIB Token {
538538
void isAttributeNothrow(const bool value) {
539539
setFlag(fIsAttributeNothrow, value);
540540
}
541+
bool isAttributeExport() const {
542+
return getFlag(fIsAttributeExport);
543+
}
544+
void isAttributeExport(const bool value) {
545+
setFlag(fIsAttributeExport, value);
546+
}
541547
bool isAttributePacked() const {
542548
return getFlag(fIsAttributePacked);
543549
}
@@ -1278,46 +1284,47 @@ class CPPCHECKLIB Token {
12781284
Token *mLink;
12791285

12801286
enum : uint64_t {
1281-
fIsUnsigned = (1 << 0),
1282-
fIsSigned = (1 << 1),
1283-
fIsPointerCompare = (1 << 2),
1284-
fIsLong = (1 << 3),
1285-
fIsStandardType = (1 << 4),
1286-
fIsExpandedMacro = (1 << 5),
1287-
fIsCast = (1 << 6),
1288-
fIsAttributeConstructor = (1 << 7), // __attribute__((constructor)) __attribute__((constructor(priority)))
1289-
fIsAttributeDestructor = (1 << 8), // __attribute__((destructor)) __attribute__((destructor(priority)))
1290-
fIsAttributeUnused = (1 << 9), // __attribute__((unused))
1291-
fIsAttributePure = (1 << 10), // __attribute__((pure))
1292-
fIsAttributeConst = (1 << 11), // __attribute__((const))
1293-
fIsAttributeNoreturn = (1 << 12), // __attribute__((noreturn)), __declspec(noreturn)
1294-
fIsAttributeNothrow = (1 << 13), // __attribute__((nothrow)), __declspec(nothrow)
1295-
fIsAttributeUsed = (1 << 14), // __attribute__((used))
1296-
fIsAttributePacked = (1 << 15), // __attribute__((packed))
1297-
fIsAttributeMaybeUnused = (1 << 16), // [[maybe_unsed]]
1298-
fIsControlFlowKeyword = (1 << 17), // if/switch/while/...
1299-
fIsOperatorKeyword = (1 << 18), // operator=, etc
1300-
fIsComplex = (1 << 19), // complex/_Complex type
1301-
fIsEnumType = (1 << 20), // enumeration type
1302-
fIsName = (1 << 21),
1303-
fIsLiteral = (1 << 22),
1304-
fIsTemplateArg = (1 << 23),
1305-
fIsAttributeNodiscard = (1 << 24), // __attribute__ ((warn_unused_result)), [[nodiscard]]
1306-
fAtAddress = (1 << 25), // @ 0x4000
1307-
fIncompleteVar = (1 << 26),
1308-
fConstexpr = (1 << 27),
1309-
fExternC = (1 << 28),
1310-
fIsSplitVarDeclComma = (1 << 29), // set to true when variable declarations are split up ('int a,b;' => 'int a; int b;')
1311-
fIsSplitVarDeclEq = (1 << 30), // set to true when variable declaration with initialization is split up ('int a=5;' => 'int a; a=5;')
1312-
fIsImplicitInt = (1U << 31), // Is "int" token implicitly added?
1313-
fIsInline = (1ULL << 32), // Is this a inline type
1314-
fIsTemplate = (1ULL << 33),
1315-
fIsSimplifedScope = (1ULL << 34), // scope added when simplifying e.g. if (int i = ...; ...)
1316-
fIsRemovedVoidParameter = (1ULL << 35), // A void function parameter has been removed
1317-
fIsIncompleteConstant = (1ULL << 36),
1318-
fIsRestrict = (1ULL << 37), // Is this a restrict pointer type
1319-
fIsSimplifiedTypedef = (1ULL << 38),
1320-
fIsFinalType = (1ULL << 39), // Is this a type with final specifier
1287+
fIsUnsigned = (1ULL << 0),
1288+
fIsSigned = (1ULL << 1),
1289+
fIsPointerCompare = (1ULL << 2),
1290+
fIsLong = (1ULL << 3),
1291+
fIsStandardType = (1ULL << 4),
1292+
fIsExpandedMacro = (1ULL << 5),
1293+
fIsCast = (1ULL << 6),
1294+
fIsAttributeConstructor = (1ULL << 7), // __attribute__((constructor)) __attribute__((constructor(priority)))
1295+
fIsAttributeDestructor = (1ULL << 8), // __attribute__((destructor)) __attribute__((destructor(priority)))
1296+
fIsAttributeUnused = (1ULL << 9), // __attribute__((unused))
1297+
fIsAttributePure = (1ULL << 10), // __attribute__((pure))
1298+
fIsAttributeConst = (1ULL << 11), // __attribute__((const))
1299+
fIsAttributeNoreturn = (1ULL << 12), // __attribute__((noreturn)), __declspec(noreturn)
1300+
fIsAttributeNothrow = (1ULL << 13), // __attribute__((nothrow)), __declspec(nothrow)
1301+
fIsAttributeUsed = (1ULL << 14), // __attribute__((used))
1302+
fIsAttributePacked = (1ULL << 15), // __attribute__((packed))
1303+
fIsAttributeExport = (1ULL << 16), // __attribute__((__visibility__("default"))), __declspec(dllexport)
1304+
fIsAttributeMaybeUnused = (1ULL << 17), // [[maybe_unsed]]
1305+
fIsControlFlowKeyword = (1ULL << 18), // if/switch/while/...
1306+
fIsOperatorKeyword = (1ULL << 19), // operator=, etc
1307+
fIsComplex = (1ULL << 20), // complex/_Complex type
1308+
fIsEnumType = (1ULL << 21), // enumeration type
1309+
fIsName = (1ULL << 22),
1310+
fIsLiteral = (1ULL << 23),
1311+
fIsTemplateArg = (1ULL << 24),
1312+
fIsAttributeNodiscard = (1ULL << 25), // __attribute__ ((warn_unused_result)), [[nodiscard]]
1313+
fAtAddress = (1ULL << 26), // @ 0x4000
1314+
fIncompleteVar = (1ULL << 27),
1315+
fConstexpr = (1ULL << 28),
1316+
fExternC = (1ULL << 29),
1317+
fIsSplitVarDeclComma = (1ULL << 30), // set to true when variable declarations are split up ('int a,b;' => 'int a; int b;')
1318+
fIsSplitVarDeclEq = (1ULL << 31), // set to true when variable declaration with initialization is split up ('int a=5;' => 'int a; a=5;')
1319+
fIsImplicitInt = (1ULL << 32), // Is "int" token implicitly added?
1320+
fIsInline = (1ULL << 33), // Is this a inline type
1321+
fIsTemplate = (1ULL << 34),
1322+
fIsSimplifedScope = (1ULL << 35), // scope added when simplifying e.g. if (int i = ...; ...)
1323+
fIsRemovedVoidParameter = (1ULL << 36), // A void function parameter has been removed
1324+
fIsIncompleteConstant = (1ULL << 37),
1325+
fIsRestrict = (1ULL << 38), // Is this a restrict pointer type
1326+
fIsSimplifiedTypedef = (1ULL << 39),
1327+
fIsFinalType = (1ULL << 40), // Is this a type with final specifier
13211328
};
13221329

13231330
Token::Type mTokType;

lib/tokenize.cpp

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5802,6 +5802,8 @@ void Tokenizer::dump(std::ostream &out) const
58025802
out << " isComplex=\"true\"";
58035803
if (tok->isRestrict())
58045804
out << " isRestrict=\"true\"";
5805+
if (tok->isAttributeExport())
5806+
out << " isAttributeExport=\"true\"";
58055807
if (tok->link())
58065808
out << " link=\"" << tok->link() << '\"';
58075809
if (tok->varId() > 0)
@@ -8688,20 +8690,65 @@ void Tokenizer::simplifyCallingConvention()
86888690
}
86898691
}
86908692

8693+
static bool isAttribute(const Token* tok, bool gcc) {
8694+
return gcc ? Token::Match(tok, "__attribute__|__attribute (") : Token::Match(tok, "__declspec|_declspec (");
8695+
}
8696+
8697+
static Token* getTokenAfterAttributes(Token* tok, bool gccattr) {
8698+
Token* after = tok;
8699+
while (isAttribute(after, gccattr))
8700+
after = after->linkAt(1)->next();
8701+
return after;
8702+
}
8703+
8704+
Token* Tokenizer::getAttributeFuncTok(Token* tok, bool gccattr) const {
8705+
if (!Token::Match(tok, "%name% ("))
8706+
return nullptr;
8707+
Token* const after = getTokenAfterAttributes(tok, gccattr);
8708+
if (!after)
8709+
syntaxError(tok);
8710+
8711+
if (Token::Match(after, "%name%|*|&|(")) {
8712+
Token *ftok = after;
8713+
while (Token::Match(ftok, "%name%|::|<|*|& !!(")) {
8714+
if (ftok->str() == "<") {
8715+
ftok = ftok->findClosingBracket();
8716+
if (!ftok)
8717+
break;
8718+
}
8719+
ftok = ftok->next();
8720+
}
8721+
if (Token::simpleMatch(ftok, "( *"))
8722+
ftok = ftok->tokAt(2);
8723+
if (Token::Match(ftok, "%name% (|)"))
8724+
return ftok;
8725+
} else if (Token::Match(after, "[;{=:]")) {
8726+
Token *prev = tok->previous();
8727+
while (Token::Match(prev, "%name%"))
8728+
prev = prev->previous();
8729+
if (Token::simpleMatch(prev, ")") && Token::Match(prev->link()->previous(), "%name% ("))
8730+
return prev->link()->previous();
8731+
else if (Token::simpleMatch(prev, ")") && Token::Match(prev->link()->tokAt(-2), "operator %op% (") && isCPP())
8732+
return prev->link()->tokAt(-2);
8733+
else if ((!prev || Token::Match(prev, "[;{}*]")) && Token::Match(tok->previous(), "%name%"))
8734+
return tok->previous();
8735+
}
8736+
return nullptr;
8737+
}
8738+
86918739
void Tokenizer::simplifyDeclspec()
86928740
{
86938741
for (Token *tok = list.front(); tok; tok = tok->next()) {
8694-
while (Token::Match(tok, "__declspec|_declspec (") && tok->next()->link() && tok->next()->link()->next()) {
8695-
if (Token::Match(tok->tokAt(2), "noreturn|nothrow")) {
8696-
Token *tok1 = tok->next()->link()->next();
8697-
while (tok1 && !Token::Match(tok1, "%name%")) {
8698-
tok1 = tok1->next();
8699-
}
8700-
if (tok1) {
8742+
while (isAttribute(tok, false)) {
8743+
Token *functok = getAttributeFuncTok(tok, false);
8744+
if (Token::Match(tok->tokAt(2), "noreturn|nothrow|dllexport")) {
8745+
if (functok) {
87018746
if (tok->strAt(2) == "noreturn")
8702-
tok1->isAttributeNoreturn(true);
8747+
functok->isAttributeNoreturn(true);
8748+
else if (tok->strAt(2) == "nothrow")
8749+
functok->isAttributeNothrow(true);
87038750
else
8704-
tok1->isAttributeNothrow(true);
8751+
functok->isAttributeExport(true);
87058752
}
87068753
} else if (tok->strAt(2) == "property")
87078754
tok->next()->link()->insertToken("__property");
@@ -8721,39 +8768,8 @@ void Tokenizer::simplifyAttribute()
87218768
if (mSettings->library.isFunctionConst(tok->str(), false))
87228769
tok->isAttributeConst(true);
87238770
}
8724-
while (Token::Match(tok, "__attribute__|__attribute (")) {
8725-
Token *after = tok;
8726-
while (Token::Match(after, "__attribute__|__attribute ("))
8727-
after = after->linkAt(1)->next();
8728-
if (!after)
8729-
syntaxError(tok);
8730-
8731-
Token *functok = nullptr;
8732-
if (Token::Match(after, "%name%|*|&|(")) {
8733-
Token *ftok = after;
8734-
while (Token::Match(ftok, "%name%|::|<|*|& !!(")) {
8735-
if (ftok->str() == "<") {
8736-
ftok = ftok->findClosingBracket();
8737-
if (!ftok)
8738-
break;
8739-
}
8740-
ftok = ftok->next();
8741-
}
8742-
if (Token::simpleMatch(ftok, "( *"))
8743-
ftok = ftok->tokAt(2);
8744-
if (Token::Match(ftok, "%name% (|)"))
8745-
functok = ftok;
8746-
} else if (Token::Match(after, "[;{=:]")) {
8747-
Token *prev = tok->previous();
8748-
while (Token::Match(prev, "%name%"))
8749-
prev = prev->previous();
8750-
if (Token::simpleMatch(prev, ")") && Token::Match(prev->link()->previous(), "%name% ("))
8751-
functok = prev->link()->previous();
8752-
else if (Token::simpleMatch(prev, ")") && Token::Match(prev->link()->tokAt(-2), "operator %op% (") && isCPP())
8753-
functok = prev->link()->tokAt(-2);
8754-
else if ((!prev || Token::Match(prev, "[;{}*]")) && Token::Match(tok->previous(), "%name%"))
8755-
functok = tok->previous();
8756-
}
8771+
while (isAttribute(tok, true)) {
8772+
Token *functok = getAttributeFuncTok(tok, true);
87578773

87588774
for (Token *attr = tok->tokAt(2); attr->str() != ")"; attr = attr->next()) {
87598775
if (Token::Match(attr, "%name% ("))
@@ -8773,6 +8789,7 @@ void Tokenizer::simplifyAttribute()
87738789

87748790
else if (Token::Match(attr, "[(,] unused|__unused__|used|__used__ [,)]")) {
87758791
Token *vartok = nullptr;
8792+
Token *after = getTokenAfterAttributes(tok, true);
87768793

87778794
// check if after variable name
87788795
if (Token::Match(after, ";|=")) {
@@ -8812,6 +8829,9 @@ void Tokenizer::simplifyAttribute()
88128829

88138830
else if (Token::Match(attr, "[(,] packed [,)]") && Token::simpleMatch(tok->previous(), "}"))
88148831
tok->previous()->isAttributePacked(true);
8832+
8833+
else if (functok && Token::simpleMatch(attr, "( __visibility__ ( \"default\" ) )"))
8834+
functok->isAttributeExport(true);
88158835
}
88168836

88178837
Token::eraseTokens(tok, tok->linkAt(1)->next());

lib/tokenize.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,9 @@ class CPPCHECKLIB Tokenizer {
467467
*/
468468
void simplifyAttribute();
469469

470+
/** Get function token for a attribute */
471+
Token* getAttributeFuncTok(Token* tok, bool gccattr) const;
472+
470473
/**
471474
* Remove \__cppcheck\__ ((?))
472475
*/

test/testtokenize.cpp

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ class TestTokenizer : public TestFixture {
260260
TEST_CASE(functionAttributeBefore2);
261261
TEST_CASE(functionAttributeBefore3);
262262
TEST_CASE(functionAttributeBefore4);
263+
TEST_CASE(functionAttributeBefore5); // __declspec(dllexport)
263264
TEST_CASE(functionAttributeAfter1);
264265
TEST_CASE(functionAttributeAfter2);
265266
TEST_CASE(functionAttributeListBefore);
@@ -3691,8 +3692,9 @@ class TestTokenizer : public TestFixture {
36913692
"void __attribute__((__pure__)) __attribute__((__nothrow__)) __attribute__((__const__)) func2();\n"
36923693
"void __attribute__((nothrow)) __attribute__((pure)) __attribute__((const)) func3();\n"
36933694
"void __attribute__((__nothrow__)) __attribute__((__pure__)) __attribute__((__const__)) func4();\n"
3694-
"void __attribute__((noreturn)) func5();";
3695-
const char expected[] = "void func1 ( ) ; void func2 ( ) ; void func3 ( ) ; void func4 ( ) ; void func5 ( ) ;";
3695+
"void __attribute__((noreturn)) func5();\n"
3696+
"void __attribute__((__visibility__(\"default\"))) func6();";
3697+
const char expected[] = "void func1 ( ) ; void func2 ( ) ; void func3 ( ) ; void func4 ( ) ; void func5 ( ) ; void func6 ( ) ;";
36963698

36973699
errout.str("");
36983700

@@ -3709,12 +3711,14 @@ class TestTokenizer : public TestFixture {
37093711
const Token * func3 = Token::findsimplematch(tokenizer.tokens(), "func3");
37103712
const Token * func4 = Token::findsimplematch(tokenizer.tokens(), "func4");
37113713
const Token * func5 = Token::findsimplematch(tokenizer.tokens(), "func5");
3714+
const Token * func6 = Token::findsimplematch(tokenizer.tokens(), "func6");
37123715

3713-
ASSERT(func1 && func1->isAttributePure() && func1->isAttributeNothrow() && func1->isAttributeConst());
3714-
ASSERT(func2 && func2->isAttributePure() && func2->isAttributeNothrow() && func2->isAttributeConst());
3715-
ASSERT(func3 && func3->isAttributePure() && func3->isAttributeNothrow() && func3->isAttributeConst());
3716-
ASSERT(func4 && func4->isAttributePure() && func4->isAttributeNothrow() && func4->isAttributeConst());
3716+
ASSERT(func1 && func1->isAttributePure() && func1->isAttributeNothrow() && func1->isAttributeConst() && !func1->isAttributeExport());
3717+
ASSERT(func2 && func2->isAttributePure() && func2->isAttributeNothrow() && func2->isAttributeConst() && !func2->isAttributeExport());
3718+
ASSERT(func3 && func3->isAttributePure() && func3->isAttributeNothrow() && func3->isAttributeConst() && !func3->isAttributeExport());
3719+
ASSERT(func4 && func4->isAttributePure() && func4->isAttributeNothrow() && func4->isAttributeConst() && !func4->isAttributeExport());
37173720
ASSERT(func5 && func5->isAttributeNoreturn());
3721+
ASSERT(func6 && func6->isAttributeExport());
37183722
}
37193723

37203724
void functionAttributeBefore2() {
@@ -3765,6 +3769,25 @@ class TestTokenizer : public TestFixture {
37653769
ASSERT(foo && foo->isAttributeConst());
37663770
}
37673771

3772+
void functionAttributeBefore5() { // __declspec(dllexport)
3773+
const char code[] = "void __declspec(dllexport) func1();\n";
3774+
const char expected[] = "void func1 ( ) ;";
3775+
3776+
errout.str("");
3777+
3778+
// tokenize..
3779+
Tokenizer tokenizer(&settings0, this);
3780+
std::istringstream istr(code);
3781+
ASSERT(tokenizer.tokenize(istr, "test.cpp"));
3782+
3783+
// Expected result..
3784+
ASSERT_EQUALS(expected, tokenizer.tokens()->stringifyList(nullptr, false));
3785+
3786+
const Token * func1 = Token::findsimplematch(tokenizer.tokens(), "func1");
3787+
3788+
ASSERT(func1 && func1->isAttributeExport());
3789+
}
3790+
37683791
void functionAttributeAfter1() {
37693792
const char code[] = "void func1() __attribute__((pure)) __attribute__((nothrow)) __attribute__((const));\n"
37703793
"void func2() __attribute__((__pure__)) __attribute__((__nothrow__)) __attribute__((__const__));\n"

0 commit comments

Comments
 (0)