Skip to content

Commit 0583763

Browse files
committed
Fixed cppcheck-opensource#3088 (False positive: Dont report "struct or union member is never used" for structs with __attribute__((packed)) or #pragma pack(push))
1 parent 0f20506 commit 0583763

6 files changed

Lines changed: 59 additions & 17 deletions

File tree

lib/checkunusedvar.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "checkunusedvar.h"
2222

2323
#include "astutils.h"
24+
#include "preprocessor.h"
2425
#include "settings.h"
2526
#include "symboldatabase.h"
2627
#include "token.h"
@@ -1340,6 +1341,17 @@ void CheckUnusedVar::checkStructMemberUsage()
13401341
// Packed struct => possibly used by lowlevel code. Struct members might be required by hardware.
13411342
if (scope.bodyEnd->isAttributePacked())
13421343
continue;
1344+
if (const Preprocessor *preprocessor = mTokenizer->getPreprocessor()) {
1345+
bool isPacked = false;
1346+
for (const Directive &d: preprocessor->getDirectives()) {
1347+
if (d.str == "#pragma pack(1)" && d.file == mTokenizer->list.getFiles().front() && d.linenr < scope.bodyStart->linenr()) {
1348+
isPacked=true;
1349+
break;
1350+
}
1351+
}
1352+
if (isPacked)
1353+
continue;
1354+
}
13431355

13441356
// Bail out if struct/union contains any functions
13451357
if (!scope.functionList.empty())

lib/cppcheck.cpp

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -688,16 +688,17 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
688688
continue;
689689
}
690690

691-
Tokenizer mTokenizer(&mSettings, this);
691+
Tokenizer tokenizer(&mSettings, this);
692+
tokenizer.setPreprocessor(&preprocessor);
692693
if (mSettings.showtime != SHOWTIME_MODES::SHOWTIME_NONE)
693-
mTokenizer.setTimerResults(&s_timerResults);
694+
tokenizer.setTimerResults(&s_timerResults);
694695

695696
try {
696697
// Create tokens, skip rest of iteration if failed
697698
{
698699
Timer timer("Tokenizer::createTokens", mSettings.showtime, &s_timerResults);
699700
simplecpp::TokenList tokensP = preprocessor.preprocess(tokens1, mCurrentConfig, files, true);
700-
mTokenizer.createTokens(std::move(tokensP));
701+
tokenizer.createTokens(std::move(tokensP));
701702
}
702703
hasValidConfig = true;
703704

@@ -708,19 +709,19 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
708709
mErrorLogger.reportOut("Checking " + fixedpath + ": " + mCurrentConfig + "...");
709710
}
710711

711-
if (!mTokenizer.tokens())
712+
if (!tokenizer.tokens())
712713
continue;
713714

714715
// skip rest of iteration if just checking configuration
715716
if (mSettings.checkConfiguration)
716717
continue;
717718

718719
// Check raw tokens
719-
checkRawTokens(mTokenizer);
720+
checkRawTokens(tokenizer);
720721

721722
// Simplify tokens into normal form, skip rest of iteration if failed
722723
Timer timer2("Tokenizer::simplifyTokens1", mSettings.showtime, &s_timerResults);
723-
bool result = mTokenizer.simplifyTokens1(mCurrentConfig);
724+
bool result = tokenizer.simplifyTokens1(mCurrentConfig);
724725
timer2.stop();
725726
if (!result)
726727
continue;
@@ -733,13 +734,13 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
733734
fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>" << std::endl;
734735
fdump << " </standards>" << std::endl;
735736
preprocessor.dump(fdump);
736-
mTokenizer.dump(fdump);
737+
tokenizer.dump(fdump);
737738
fdump << "</dump>" << std::endl;
738739
}
739740

740741
// Skip if we already met the same simplified token list
741742
if (mSettings.force || mSettings.maxConfigs > 1) {
742-
const unsigned long long checksum = mTokenizer.list.calculateChecksum();
743+
const unsigned long long checksum = tokenizer.list.calculateChecksum();
743744
if (checksums.find(checksum) != checksums.end()) {
744745
if (mSettings.debugwarnings)
745746
purgedConfigurationMessage(filename, mCurrentConfig);
@@ -749,25 +750,25 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
749750
}
750751

751752
// Check normal tokens
752-
checkNormalTokens(mTokenizer);
753+
checkNormalTokens(tokenizer);
753754

754755
// Analyze info..
755756
if (!mSettings.buildDir.empty())
756-
checkUnusedFunctions.parseTokens(mTokenizer, filename.c_str(), &mSettings);
757+
checkUnusedFunctions.parseTokens(tokenizer, filename.c_str(), &mSettings);
757758

758759
// simplify more if required, skip rest of iteration if failed
759760
if (mSimplify && hasRule("simple")) {
760761
std::cout << "Handling of \"simple\" rules is deprecated and will be removed in Cppcheck 2.5." << std::endl;
761762

762763
// if further simplification fails then skip rest of iteration
763764
Timer timer3("Tokenizer::simplifyTokenList2", mSettings.showtime, &s_timerResults);
764-
result = mTokenizer.simplifyTokenList2();
765+
result = tokenizer.simplifyTokenList2();
765766
timer3.stop();
766767
if (!result)
767768
continue;
768769

769770
if (!Settings::terminated())
770-
executeRules("simple", mTokenizer);
771+
executeRules("simple", tokenizer);
771772
}
772773

773774
} catch (const simplecpp::Output &o) {
@@ -779,16 +780,16 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
779780
} catch (const InternalError &e) {
780781
std::list<ErrorMessage::FileLocation> locationList;
781782
if (e.token) {
782-
ErrorMessage::FileLocation loc(e.token, &mTokenizer.list);
783+
ErrorMessage::FileLocation loc(e.token, &tokenizer.list);
783784
locationList.push_back(loc);
784785
} else {
785-
ErrorMessage::FileLocation loc(mTokenizer.list.getSourceFilePath(), 0, 0);
786+
ErrorMessage::FileLocation loc(tokenizer.list.getSourceFilePath(), 0, 0);
786787
ErrorMessage::FileLocation loc2(filename, 0, 0);
787788
locationList.push_back(loc2);
788789
locationList.push_back(loc);
789790
}
790791
ErrorMessage errmsg(locationList,
791-
mTokenizer.list.getSourceFilePath(),
792+
tokenizer.list.getSourceFilePath(),
792793
Severity::error,
793794
e.errorMessage,
794795
e.id,

lib/preprocessor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class CPPCHECKLIB Preprocessor {
9191
void inlineSuppressions(const simplecpp::TokenList &tokens);
9292

9393
void setDirectives(const simplecpp::TokenList &tokens);
94+
void setDirectives(const std::list<Directive> &directives) {
95+
mDirectives = directives;
96+
}
9497

9598
/** list of all directives met while preprocessing file */
9699
const std::list<Directive> &getDirectives() const {

lib/tokenize.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,9 @@ Tokenizer::Tokenizer() :
157157
mCodeWithTemplates(false), //is there any templates?
158158
mTimerResults(nullptr)
159159
#ifdef MAXTIME
160-
,mMaxTime(std::time(0) + MAXTIME)
160+
, mMaxTime(std::time(0) + MAXTIME)
161161
#endif
162+
, mPreprocessor(nullptr)
162163
{
163164
}
164165

@@ -175,6 +176,7 @@ Tokenizer::Tokenizer(const Settings *settings, ErrorLogger *errorLogger) :
175176
#ifdef MAXTIME
176177
,mMaxTime(std::time(0) + MAXTIME)
177178
#endif
179+
, mPreprocessor(nullptr)
178180
{
179181
// make sure settings are specified
180182
assert(mSettings);

lib/tokenize.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class TimerResults;
3737
class Token;
3838
class TemplateSimplifier;
3939
class ErrorLogger;
40+
class Preprocessor;
4041

4142
namespace simplecpp {
4243
class TokenList;
@@ -560,6 +561,13 @@ class CPPCHECKLIB Tokenizer {
560561
*/
561562
static const Token * isFunctionHead(const Token *tok, const std::string &endsWith, bool cpp);
562563

564+
void setPreprocessor(const Preprocessor *preprocessor) {
565+
mPreprocessor = preprocessor;
566+
}
567+
const Preprocessor *getPreprocessor() const {
568+
return mPreprocessor;
569+
}
570+
563571
private:
564572

565573
/**
@@ -956,6 +964,8 @@ class CPPCHECKLIB Tokenizer {
956964
/** Tokenizer maxtime */
957965
const std::time_t mMaxTime;
958966
#endif
967+
968+
const Preprocessor *mPreprocessor;
959969
};
960970

961971
/// @}

test/testunusedvar.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818

1919
#include "checkunusedvar.h"
20+
#include "preprocessor.h"
2021
#include "settings.h"
2122
#include "testsuite.h"
2223
#include "tokenize.h"
@@ -55,6 +56,7 @@ class TestUnusedVar : public TestFixture {
5556
TEST_CASE(structmember12); // #7179 - FP unused structmember
5657
TEST_CASE(structmember13); // #3088 - __attribute__((packed))
5758
TEST_CASE(structmember14); // #6508 - (struct x){1,2,..}
59+
TEST_CASE(structmember15); // #3088 - #pragma pack(1)
5860
TEST_CASE(structmember_sizeof);
5961

6062
TEST_CASE(localvar1);
@@ -211,12 +213,17 @@ class TestUnusedVar : public TestFixture {
211213
TEST_CASE(volatileData); // #9280
212214
}
213215

214-
void checkStructMemberUsage(const char code[]) {
216+
void checkStructMemberUsage(const char code[], const std::list<Directive> *directives=nullptr) {
215217
// Clear the error buffer..
216218
errout.str("");
217219

220+
Preprocessor preprocessor(settings, nullptr);
221+
if (directives)
222+
preprocessor.setDirectives(*directives);
223+
218224
// Tokenize..
219225
Tokenizer tokenizer(&settings, this);
226+
tokenizer.setPreprocessor(&preprocessor);
220227
std::istringstream istr(code);
221228
tokenizer.tokenize(istr, "test.cpp");
222229

@@ -473,6 +480,13 @@ class TestUnusedVar : public TestFixture {
473480
ASSERT_EQUALS("", errout.str());
474481
}
475482

483+
void structmember15() { // #3088
484+
std::list<Directive> directives;
485+
directives.emplace_back("test.cpp", 1, "#pragma pack(1)");
486+
checkStructMemberUsage("\nstruct Foo { int x; int y; };", &directives);
487+
ASSERT_EQUALS("", errout.str());
488+
}
489+
476490
void structmember_extern() {
477491
// extern struct => no false positive
478492
checkStructMemberUsage("extern struct AB\n"

0 commit comments

Comments
 (0)