Skip to content

Commit b3c7f00

Browse files
authored
Fix #13333 (better handling when source file is repeated in compile_commands.json) (#7508)
1 parent d655a3a commit b3c7f00

17 files changed

+225
-64
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ test/options.o: test/options.cpp test/options.h
701701
test/test64bit.o: test/test64bit.cpp lib/addoninfo.h lib/check.h lib/check64bit.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h
702702
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/test64bit.cpp
703703

704-
test/testanalyzerinformation.o: test/testanalyzerinformation.cpp lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/utils.h test/fixture.h
704+
test/testanalyzerinformation.o: test/testanalyzerinformation.cpp lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/utils.h test/fixture.h
705705
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testanalyzerinformation.cpp
706706

707707
test/testassert.o: test/testassert.cpp lib/addoninfo.h lib/check.h lib/checkassert.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h

gui/checkthread.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ void CheckThread::runAddonsAndTools(const Settings& settings, const FileSettings
245245

246246
const std::string &buildDir = settings.buildDir;
247247
if (!buildDir.empty()) {
248-
analyzerInfoFile = QString::fromStdString(AnalyzerInformation::getAnalyzerInfoFile(buildDir, fileSettings->filename(), fileSettings->cfg));
248+
analyzerInfoFile = QString::fromStdString(AnalyzerInformation::getAnalyzerInfoFile(buildDir, fileSettings->filename(), fileSettings->cfg, fileSettings->fileIndex));
249249

250250
QStringList args2(args);
251251
args2.insert(0,"-E");

lib/analyzerinfo.cpp

+51-12
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include <cstring>
2727
#include <map>
28+
#include <sstream>
2829

2930
#include "xml.h"
3031

@@ -47,21 +48,30 @@ static std::string getFilename(const std::string &fullpath)
4748

4849
void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std::list<std::string> &sourcefiles, const std::string &userDefines, const std::list<FileSettings> &fileSettings)
4950
{
50-
std::map<std::string, unsigned int> fileCount;
51-
5251
const std::string filesTxt(buildDir + "/files.txt");
5352
std::ofstream fout(filesTxt);
53+
fout << getFilesTxt(sourcefiles, userDefines, fileSettings);
54+
}
55+
56+
std::string AnalyzerInformation::getFilesTxt(const std::list<std::string> &sourcefiles, const std::string &userDefines, const std::list<FileSettings> &fileSettings) {
57+
std::ostringstream ret;
58+
59+
std::map<std::string, unsigned int> fileCount;
60+
5461
for (const std::string &f : sourcefiles) {
5562
const std::string afile = getFilename(f);
56-
fout << afile << ".a" << (++fileCount[afile]) << "::" << Path::simplifyPath(f) << '\n';
63+
ret << afile << ".a" << (++fileCount[afile]) << sep << sep << sep << Path::simplifyPath(f) << '\n';
5764
if (!userDefines.empty())
58-
fout << afile << ".a" << (++fileCount[afile]) << ":" << userDefines << ":" << Path::simplifyPath(f) << '\n';
65+
ret << afile << ".a" << (++fileCount[afile]) << sep << userDefines << sep << sep << Path::simplifyPath(f) << '\n';
5966
}
6067

6168
for (const FileSettings &fs : fileSettings) {
6269
const std::string afile = getFilename(fs.filename());
63-
fout << afile << ".a" << (++fileCount[afile]) << ":" << fs.cfg << ":" << Path::simplifyPath(fs.filename()) << std::endl;
70+
const std::string id = fs.fileIndex > 0 ? std::to_string(fs.fileIndex) : "";
71+
ret << afile << ".a" << (++fileCount[afile]) << sep << fs.cfg << sep << id << sep << Path::simplifyPath(fs.filename()) << std::endl;
6472
}
73+
74+
return ret.str();
6575
}
6676

6777
void AnalyzerInformation::close()
@@ -96,25 +106,26 @@ static bool skipAnalysis(const std::string &analyzerInfoFile, std::size_t hash,
96106
return true;
97107
}
98108

99-
std::string AnalyzerInformation::getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg)
109+
std::string AnalyzerInformation::getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg, int fileIndex)
100110
{
111+
const std::string id = (fileIndex > 0) ? std::to_string(fileIndex) : "";
101112
std::string line;
102-
const std::string end(':' + cfg + ':' + Path::simplifyPath(sourcefile));
113+
const std::string end(sep + cfg + sep + id + sep + Path::simplifyPath(sourcefile));
103114
while (std::getline(filesTxt,line)) {
104115
if (line.size() <= end.size() + 2U)
105116
continue;
106117
if (!endsWith(line, end.c_str(), end.size()))
107118
continue;
108-
return line.substr(0,line.find(':'));
119+
return line.substr(0,line.find(sep));
109120
}
110121
return "";
111122
}
112123

113-
std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg)
124+
std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, int fileIndex)
114125
{
115126
std::ifstream fin(Path::join(buildDir, "files.txt"));
116127
if (fin.is_open()) {
117-
const std::string& ret = getAnalyzerInfoFileFromFilesTxt(fin, sourcefile, cfg);
128+
const std::string& ret = getAnalyzerInfoFileFromFilesTxt(fin, sourcefile, cfg, fileIndex);
118129
if (!ret.empty())
119130
return Path::join(buildDir, ret);
120131
}
@@ -128,13 +139,13 @@ std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir
128139
return Path::join(buildDir, filename) + ".analyzerinfo";
129140
}
130141

131-
bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list<ErrorMessage> &errors)
142+
bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, int fileIndex, std::size_t hash, std::list<ErrorMessage> &errors)
132143
{
133144
if (buildDir.empty() || sourcefile.empty())
134145
return true;
135146
close();
136147

137-
mAnalyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfg);
148+
mAnalyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfg,fileIndex);
138149

139150
if (skipAnalysis(mAnalyzerInfoFile, hash, errors))
140151
return false;
@@ -161,3 +172,31 @@ void AnalyzerInformation::setFileInfo(const std::string &check, const std::strin
161172
if (mOutputStream.is_open() && !fileInfo.empty())
162173
mOutputStream << " <FileInfo check=\"" << check << "\">\n" << fileInfo << " </FileInfo>\n";
163174
}
175+
176+
bool AnalyzerInformation::Info::parse(const std::string& filesTxtLine) {
177+
const std::string::size_type sep1 = filesTxtLine.find(sep);
178+
if (sep1 == std::string::npos)
179+
return false;
180+
const std::string::size_type sep2 = filesTxtLine.find(sep, sep1+1);
181+
if (sep2 == std::string::npos)
182+
return false;
183+
const std::string::size_type sep3 = filesTxtLine.find(sep, sep2+1);
184+
if (sep3 == std::string::npos)
185+
return false;
186+
187+
if (sep3 == sep2 + 1)
188+
fileIndex = 0;
189+
else {
190+
try {
191+
fileIndex = std::stoi(filesTxtLine.substr(sep2+1, sep3-sep2-1));
192+
} catch (const std::exception&) {
193+
return false;
194+
}
195+
}
196+
197+
afile = filesTxtLine.substr(0, sep1);
198+
cfg = filesTxtLine.substr(sep1+1, sep2-sep1-1);
199+
sourceFile = filesTxtLine.substr(sep3+1);
200+
return true;
201+
}
202+

lib/analyzerinfo.h

+17-3
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,30 @@ class CPPCHECKLIB AnalyzerInformation {
5151
public:
5252
~AnalyzerInformation();
5353

54+
static std::string getFilesTxt(const std::list<std::string> &sourcefiles, const std::string &userDefines, const std::list<FileSettings> &fileSettings);
55+
5456
static void writeFilesTxt(const std::string &buildDir, const std::list<std::string> &sourcefiles, const std::string &userDefines, const std::list<FileSettings> &fileSettings);
5557

5658
/** Close current TU.analyzerinfo file */
5759
void close();
58-
bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list<ErrorMessage> &errors);
60+
bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, int fileIndex, std::size_t hash, std::list<ErrorMessage> &errors);
5961
void reportErr(const ErrorMessage &msg);
6062
void setFileInfo(const std::string &check, const std::string &fileInfo);
61-
static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg);
63+
static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, int fileIndex);
64+
65+
static const char sep = ':';
66+
67+
class CPPCHECKLIB Info {
68+
public:
69+
bool parse(const std::string& filesTxtLine);
70+
std::string afile;
71+
std::string cfg;
72+
int fileIndex = 0;
73+
std::string sourceFile;
74+
};
75+
6276
protected:
63-
static std::string getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg);
77+
static std::string getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg, int fileIndex);
6478
private:
6579
std::ofstream mOutputStream;
6680
std::string mAnalyzerInfoFile;

lib/cppcheck.cpp

+29-31
Original file line numberDiff line numberDiff line change
@@ -331,16 +331,16 @@ static std::vector<std::string> split(const std::string &str, const std::string
331331
return ret;
332332
}
333333

334-
static std::string getDumpFileName(const Settings& settings, const std::string& filename)
334+
static std::string getDumpFileName(const Settings& settings, const std::string& filename, int fileIndex)
335335
{
336-
std::string extension;
337-
if (settings.dump || !settings.buildDir.empty())
338-
extension = ".dump";
339-
else
340-
extension = "." + std::to_string(settings.pid) + ".dump";
336+
std::string extension = ".dump";
337+
if (fileIndex > 0)
338+
extension = "." + std::to_string(fileIndex) + extension;
339+
if (!settings.dump && settings.buildDir.empty())
340+
extension = "." + std::to_string(settings.pid) + extension;
341341

342342
if (!settings.dump && !settings.buildDir.empty())
343-
return AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, filename, "") + extension;
343+
return AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, filename, "", fileIndex) + extension;
344344
return filename + extension;
345345
}
346346

@@ -351,12 +351,13 @@ static std::string getCtuInfoFileName(const std::string &dumpFile)
351351

352352
static void createDumpFile(const Settings& settings,
353353
const FileWithDetails& file,
354+
int fileIndex,
354355
std::ofstream& fdump,
355356
std::string& dumpFile)
356357
{
357358
if (!settings.dump && settings.addons.empty())
358359
return;
359-
dumpFile = getDumpFileName(settings, file.spath());
360+
dumpFile = getDumpFileName(settings, file.spath(), fileIndex);
360361

361362
fdump.open(dumpFile);
362363
if (!fdump.is_open())
@@ -649,7 +650,7 @@ static std::string getClangFlags(const Settings& setting, Standards::Language la
649650
}
650651

651652
// TODO: clear error list before returning
652-
unsigned int CppCheck::checkClang(const FileWithDetails &file)
653+
unsigned int CppCheck::checkClang(const FileWithDetails &file, int fileIndex)
653654
{
654655
// TODO: clear exitcode
655656

@@ -662,7 +663,7 @@ unsigned int CppCheck::checkClang(const FileWithDetails &file)
662663
// TODO: get language from FileWithDetails object
663664
std::string clangStderr;
664665
if (!mSettings.buildDir.empty())
665-
clangStderr = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, file.spath(), "") + ".clang-stderr";
666+
clangStderr = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, file.spath(), "", fileIndex) + ".clang-stderr";
666667

667668
std::string exe = mSettings.clangExecutable;
668669
#ifdef _WIN32
@@ -731,7 +732,7 @@ unsigned int CppCheck::checkClang(const FileWithDetails &file)
731732
// create dumpfile
732733
std::ofstream fdump;
733734
std::string dumpFile;
734-
createDumpFile(mSettings, file, fdump, dumpFile);
735+
createDumpFile(mSettings, file, fileIndex, fdump, dumpFile);
735736
if (fdump.is_open()) {
736737
fdump << getLibraryDumpData();
737738
// TODO: use tinyxml2 to create XML
@@ -775,9 +776,9 @@ unsigned int CppCheck::check(const FileWithDetails &file)
775776

776777
unsigned int returnValue;
777778
if (mSettings.clang)
778-
returnValue = checkClang(file);
779+
returnValue = checkClang(file, 0);
779780
else
780-
returnValue = checkFile(file, "");
781+
returnValue = checkFile(file, "", 0);
781782

782783
// TODO: call analyseClangTidy()
783784

@@ -787,7 +788,7 @@ unsigned int CppCheck::check(const FileWithDetails &file)
787788
unsigned int CppCheck::check(const FileWithDetails &file, const std::string &content)
788789
{
789790
std::istringstream iss(content);
790-
return checkFile(file, "", &iss);
791+
return checkFile(file, "", 0, &iss);
791792
}
792793

793794
unsigned int CppCheck::check(const FileSettings &fs)
@@ -823,7 +824,7 @@ unsigned int CppCheck::check(const FileSettings &fs)
823824
}
824825
// need to pass the externally provided ErrorLogger instead of our internal wrapper
825826
CppCheck temp(tempSettings, mSuppressions, mErrorLoggerDirect, mUseGlobalSuppressions, mExecuteCommand);
826-
const unsigned int returnValue = temp.checkFile(fs.file, fs.cfg);
827+
const unsigned int returnValue = temp.checkFile(fs.file, fs.cfg, fs.fileIndex);
827828
if (mUnusedFunctionsCheck)
828829
mUnusedFunctionsCheck->updateFunctionData(*temp.mUnusedFunctionsCheck);
829830
while (!temp.mFileInfo.empty()) {
@@ -860,7 +861,7 @@ static std::size_t calculateHash(const Preprocessor& preprocessor, const simplec
860861
return preprocessor.calculateHash(tokens, toolinfo.str());
861862
}
862863

863-
unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string &cfgname, std::istream* fileStream)
864+
unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string &cfgname, int fileIndex, std::istream* fileStream)
864865
{
865866
// TODO: move to constructor when CppCheck no longer owns the settings
866867
if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck)
@@ -937,7 +938,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string
937938
mLogger->setAnalyzerInfo(nullptr);
938939

939940
std::list<ErrorMessage> errors;
940-
analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, hash, errors);
941+
analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, fileIndex, hash, errors);
941942
analyzerInformation->setFileInfo("CheckUnusedFunctions", mUnusedFunctionsCheck->analyzerInfo(tokenizer));
942943
analyzerInformation->close();
943944
}
@@ -1011,7 +1012,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string
10111012
// Calculate hash so it can be compared with old hash / future hashes
10121013
const std::size_t hash = calculateHash(preprocessor, tokens1, mSettings, mSuppressions);
10131014
std::list<ErrorMessage> errors;
1014-
if (!analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, hash, errors)) {
1015+
if (!analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, fileIndex, hash, errors)) {
10151016
while (!errors.empty()) {
10161017
mErrorLogger.reportErr(errors.front());
10171018
errors.pop_front();
@@ -1074,7 +1075,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string
10741075
// write dump file xml prolog
10751076
std::ofstream fdump;
10761077
std::string dumpFile;
1077-
createDumpFile(mSettings, file, fdump, dumpFile);
1078+
createDumpFile(mSettings, file, fileIndex, fdump, dumpFile);
10781079
if (fdump.is_open()) {
10791080
fdump << getLibraryDumpData();
10801081
fdump << dumpProlog;
@@ -1169,7 +1170,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string
11691170
#endif
11701171

11711172
// Simplify tokens into normal form, skip rest of iteration if failed
1172-
if (!tokenizer.simplifyTokens1(currentConfig))
1173+
if (!tokenizer.simplifyTokens1(currentConfig, fileIndex))
11731174
continue;
11741175

11751176
// dump xml if --dump
@@ -1811,12 +1812,12 @@ void CppCheck::executeAddonsWholeProgram(const std::list<FileWithDetails> &files
18111812

18121813
std::vector<std::string> ctuInfoFiles;
18131814
for (const auto &f: files) {
1814-
const std::string &dumpFileName = getDumpFileName(mSettings, f.path());
1815+
const std::string &dumpFileName = getDumpFileName(mSettings, f.path(), 0);
18151816
ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName));
18161817
}
18171818

18181819
for (const auto &f: fileSettings) {
1819-
const std::string &dumpFileName = getDumpFileName(mSettings, f.filename());
1820+
const std::string &dumpFileName = getDumpFileName(mSettings, f.filename(), f.fileIndex);
18201821
ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName));
18211822
}
18221823

@@ -1943,7 +1944,7 @@ void CppCheck::analyseClangTidy(const FileSettings &fileSettings)
19431944
std::string line;
19441945

19451946
if (!mSettings.buildDir.empty()) {
1946-
const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename(), "");
1947+
const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename(), "", fileSettings.fileIndex);
19471948
std::ofstream fcmd(analyzerInfoFile + ".clang-tidy-cmd");
19481949
fcmd << istr.str();
19491950
}
@@ -2035,14 +2036,11 @@ unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const st
20352036
std::ifstream fin(filesTxt);
20362037
std::string filesTxtLine;
20372038
while (std::getline(fin, filesTxtLine)) {
2038-
const std::string::size_type firstColon = filesTxtLine.find(':');
2039-
if (firstColon == std::string::npos)
2040-
continue;
2041-
const std::string::size_type lastColon = filesTxtLine.rfind(':');
2042-
if (firstColon == lastColon)
2039+
AnalyzerInformation::Info filesTxtInfo;
2040+
if (!filesTxtInfo.parse(filesTxtLine))
20432041
continue;
2044-
const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon);
2045-
//const std::string sourcefile = filesTxtLine.substr(lastColon+1);
2042+
2043+
const std::string xmlfile = buildDir + '/' + filesTxtInfo.afile;
20462044

20472045
tinyxml2::XMLDocument doc;
20482046
const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str());
@@ -2067,7 +2065,7 @@ unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const st
20672065
for (const Check *check : Check::instances()) {
20682066
if (checkClassAttr == check->name()) {
20692067
Check::FileInfo* fi = check->loadFileInfoFromXml(e);
2070-
fi->file0 = filesTxtLine.substr(firstColon + 2);
2068+
fi->file0 = filesTxtInfo.sourceFile;
20712069
fileInfoList.push_back(fi);
20722070
}
20732071
}

lib/cppcheck.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class CPPCHECKLIB CppCheck {
169169
* @param fileStream stream the file content can be read from
170170
* @return number of errors found
171171
*/
172-
unsigned int checkFile(const FileWithDetails& file, const std::string &cfgname, std::istream* fileStream = nullptr);
172+
unsigned int checkFile(const FileWithDetails& file, const std::string &cfgname, int fileIndex, std::istream* fileStream = nullptr);
173173

174174
/**
175175
* @brief Check normal tokens
@@ -198,7 +198,7 @@ class CPPCHECKLIB CppCheck {
198198
void executeRules(const std::string &tokenlist, const TokenList &list);
199199
#endif
200200

201-
unsigned int checkClang(const FileWithDetails &file);
201+
unsigned int checkClang(const FileWithDetails &file, int fileIndex);
202202

203203
const Settings& mSettings;
204204
Suppressions& mSuppressions;

0 commit comments

Comments
 (0)