Skip to content

Fix #13333 (better handling when source file is repeated in compile_commands.json) #7508

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,7 @@ test/options.o: test/options.cpp test/options.h
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
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/test64bit.cpp

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
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
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testanalyzerinformation.cpp

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
Expand Down
2 changes: 1 addition & 1 deletion gui/checkthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ void CheckThread::runAddonsAndTools(const Settings& settings, const FileSettings

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

QStringList args2(args);
args2.insert(0,"-E");
Expand Down
63 changes: 51 additions & 12 deletions lib/analyzerinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#include <cstring>
#include <map>
#include <sstream>

#include "xml.h"

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

void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std::list<std::string> &sourcefiles, const std::string &userDefines, const std::list<FileSettings> &fileSettings)
{
std::map<std::string, unsigned int> fileCount;

const std::string filesTxt(buildDir + "/files.txt");
std::ofstream fout(filesTxt);
fout << getFilesTxt(sourcefiles, userDefines, fileSettings);
}

std::string AnalyzerInformation::getFilesTxt(const std::list<std::string> &sourcefiles, const std::string &userDefines, const std::list<FileSettings> &fileSettings) {
std::ostringstream ret;

std::map<std::string, unsigned int> fileCount;

for (const std::string &f : sourcefiles) {
const std::string afile = getFilename(f);
fout << afile << ".a" << (++fileCount[afile]) << "::" << Path::simplifyPath(f) << '\n';
ret << afile << ".a" << (++fileCount[afile]) << sep << sep << sep << Path::simplifyPath(f) << '\n';
if (!userDefines.empty())
fout << afile << ".a" << (++fileCount[afile]) << ":" << userDefines << ":" << Path::simplifyPath(f) << '\n';
ret << afile << ".a" << (++fileCount[afile]) << sep << userDefines << sep << sep << Path::simplifyPath(f) << '\n';
}

for (const FileSettings &fs : fileSettings) {
const std::string afile = getFilename(fs.filename());
fout << afile << ".a" << (++fileCount[afile]) << ":" << fs.cfg << ":" << Path::simplifyPath(fs.filename()) << std::endl;
const std::string id = fs.fileIndex > 0 ? std::to_string(fs.fileIndex) : "";
ret << afile << ".a" << (++fileCount[afile]) << sep << fs.cfg << sep << id << sep << Path::simplifyPath(fs.filename()) << std::endl;
}

return ret.str();
}

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

std::string AnalyzerInformation::getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg)
std::string AnalyzerInformation::getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg, int fileIndex)
{
const std::string id = (fileIndex > 0) ? std::to_string(fileIndex) : "";
std::string line;
const std::string end(':' + cfg + ':' + Path::simplifyPath(sourcefile));
const std::string end(sep + cfg + sep + id + sep + Path::simplifyPath(sourcefile));
while (std::getline(filesTxt,line)) {
if (line.size() <= end.size() + 2U)
continue;
if (!endsWith(line, end.c_str(), end.size()))
continue;
return line.substr(0,line.find(':'));
return line.substr(0,line.find(sep));
}
return "";
}

std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg)
std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, int fileIndex)
{
std::ifstream fin(Path::join(buildDir, "files.txt"));
if (fin.is_open()) {
const std::string& ret = getAnalyzerInfoFileFromFilesTxt(fin, sourcefile, cfg);
const std::string& ret = getAnalyzerInfoFileFromFilesTxt(fin, sourcefile, cfg, fileIndex);
if (!ret.empty())
return Path::join(buildDir, ret);
}
Expand All @@ -128,13 +139,13 @@ std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir
return Path::join(buildDir, filename) + ".analyzerinfo";
}

bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list<ErrorMessage> &errors)
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)
{
if (buildDir.empty() || sourcefile.empty())
return true;
close();

mAnalyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfg);
mAnalyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfg,fileIndex);

if (skipAnalysis(mAnalyzerInfoFile, hash, errors))
return false;
Expand All @@ -161,3 +172,31 @@ void AnalyzerInformation::setFileInfo(const std::string &check, const std::strin
if (mOutputStream.is_open() && !fileInfo.empty())
mOutputStream << " <FileInfo check=\"" << check << "\">\n" << fileInfo << " </FileInfo>\n";
}

bool AnalyzerInformation::Info::parse(const std::string& filesTxtLine) {
const std::string::size_type sep1 = filesTxtLine.find(sep);
if (sep1 == std::string::npos)
return false;
const std::string::size_type sep2 = filesTxtLine.find(sep, sep1+1);
if (sep2 == std::string::npos)
return false;
const std::string::size_type sep3 = filesTxtLine.find(sep, sep2+1);
if (sep3 == std::string::npos)
return false;

if (sep3 == sep2 + 1)
fileIndex = 0;
else {
try {
fileIndex = std::stoi(filesTxtLine.substr(sep2+1, sep3-sep2-1));
} catch (const std::exception&) {
return false;
}
}

afile = filesTxtLine.substr(0, sep1);
cfg = filesTxtLine.substr(sep1+1, sep2-sep1-1);
sourceFile = filesTxtLine.substr(sep3+1);
return true;
}

20 changes: 17 additions & 3 deletions lib/analyzerinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,30 @@ class CPPCHECKLIB AnalyzerInformation {
public:
~AnalyzerInformation();

static std::string getFilesTxt(const std::list<std::string> &sourcefiles, const std::string &userDefines, const std::list<FileSettings> &fileSettings);

static void writeFilesTxt(const std::string &buildDir, const std::list<std::string> &sourcefiles, const std::string &userDefines, const std::list<FileSettings> &fileSettings);

/** Close current TU.analyzerinfo file */
void close();
bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list<ErrorMessage> &errors);
bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, int fileIndex, std::size_t hash, std::list<ErrorMessage> &errors);
void reportErr(const ErrorMessage &msg);
void setFileInfo(const std::string &check, const std::string &fileInfo);
static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg);
static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, int fileIndex);

static const char sep = ':';

class CPPCHECKLIB Info {
public:
bool parse(const std::string& filesTxtLine);
std::string afile;
std::string cfg;
int fileIndex = 0;
std::string sourceFile;
};

protected:
static std::string getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg);
static std::string getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg, int fileIndex);
private:
std::ofstream mOutputStream;
std::string mAnalyzerInfoFile;
Expand Down
60 changes: 29 additions & 31 deletions lib/cppcheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,16 +331,16 @@ static std::vector<std::string> split(const std::string &str, const std::string
return ret;
}

static std::string getDumpFileName(const Settings& settings, const std::string& filename)
static std::string getDumpFileName(const Settings& settings, const std::string& filename, int fileIndex)
{
std::string extension;
if (settings.dump || !settings.buildDir.empty())
extension = ".dump";
else
extension = "." + std::to_string(settings.pid) + ".dump";
std::string extension = ".dump";
if (fileIndex > 0)
extension = "." + std::to_string(fileIndex) + extension;
if (!settings.dump && settings.buildDir.empty())
extension = "." + std::to_string(settings.pid) + extension;

if (!settings.dump && !settings.buildDir.empty())
return AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, filename, "") + extension;
return AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, filename, "", fileIndex) + extension;
return filename + extension;
}

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

static void createDumpFile(const Settings& settings,
const FileWithDetails& file,
int fileIndex,
std::ofstream& fdump,
std::string& dumpFile)
{
if (!settings.dump && settings.addons.empty())
return;
dumpFile = getDumpFileName(settings, file.spath());
dumpFile = getDumpFileName(settings, file.spath(), fileIndex);

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

// TODO: clear error list before returning
unsigned int CppCheck::checkClang(const FileWithDetails &file)
unsigned int CppCheck::checkClang(const FileWithDetails &file, int fileIndex)
{
// TODO: clear exitcode

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

std::string exe = mSettings.clangExecutable;
#ifdef _WIN32
Expand Down Expand Up @@ -732,7 +733,7 @@ unsigned int CppCheck::checkClang(const FileWithDetails &file)
// create dumpfile
std::ofstream fdump;
std::string dumpFile;
createDumpFile(mSettings, file, fdump, dumpFile);
createDumpFile(mSettings, file, fileIndex, fdump, dumpFile);
if (fdump.is_open()) {
fdump << getLibraryDumpData();
// TODO: use tinyxml2 to create XML
Expand Down Expand Up @@ -776,9 +777,9 @@ unsigned int CppCheck::check(const FileWithDetails &file)

unsigned int returnValue;
if (mSettings.clang)
returnValue = checkClang(file);
returnValue = checkClang(file, 0);
else
returnValue = checkFile(file, "");
returnValue = checkFile(file, "", 0);

// TODO: call analyseClangTidy()

Expand All @@ -788,7 +789,7 @@ unsigned int CppCheck::check(const FileWithDetails &file)
unsigned int CppCheck::check(const FileWithDetails &file, const std::string &content)
{
std::istringstream iss(content);
return checkFile(file, "", &iss);
return checkFile(file, "", 0, &iss);
}

unsigned int CppCheck::check(const FileSettings &fs)
Expand Down Expand Up @@ -824,7 +825,7 @@ unsigned int CppCheck::check(const FileSettings &fs)
}
// need to pass the externally provided ErrorLogger instead of our internal wrapper
CppCheck temp(tempSettings, mSuppressions, mErrorLoggerDirect, mUseGlobalSuppressions, mExecuteCommand);
const unsigned int returnValue = temp.checkFile(fs.file, fs.cfg);
const unsigned int returnValue = temp.checkFile(fs.file, fs.cfg, fs.fileIndex);
if (mUnusedFunctionsCheck)
mUnusedFunctionsCheck->updateFunctionData(*temp.mUnusedFunctionsCheck);
while (!temp.mFileInfo.empty()) {
Expand Down Expand Up @@ -861,7 +862,7 @@ static std::size_t calculateHash(const Preprocessor& preprocessor, const simplec
return preprocessor.calculateHash(tokens, toolinfo.str());
}

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

std::list<ErrorMessage> errors;
analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, hash, errors);
analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, fileIndex, hash, errors);
analyzerInformation->setFileInfo("CheckUnusedFunctions", mUnusedFunctionsCheck->analyzerInfo(tokenizer));
analyzerInformation->close();
}
Expand Down Expand Up @@ -1012,7 +1013,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string
// Calculate hash so it can be compared with old hash / future hashes
const std::size_t hash = calculateHash(preprocessor, tokens1, mSettings, mSuppressions);
std::list<ErrorMessage> errors;
if (!analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, hash, errors)) {
if (!analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, fileIndex, hash, errors)) {
while (!errors.empty()) {
mErrorLogger.reportErr(errors.front());
errors.pop_front();
Expand Down Expand Up @@ -1075,7 +1076,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string
// write dump file xml prolog
std::ofstream fdump;
std::string dumpFile;
createDumpFile(mSettings, file, fdump, dumpFile);
createDumpFile(mSettings, file, fileIndex, fdump, dumpFile);
if (fdump.is_open()) {
fdump << getLibraryDumpData();
fdump << dumpProlog;
Expand Down Expand Up @@ -1170,7 +1171,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string
#endif

// Simplify tokens into normal form, skip rest of iteration if failed
if (!tokenizer.simplifyTokens1(currentConfig))
if (!tokenizer.simplifyTokens1(currentConfig, fileIndex))
continue;

// dump xml if --dump
Expand Down Expand Up @@ -1812,12 +1813,12 @@ void CppCheck::executeAddonsWholeProgram(const std::list<FileWithDetails> &files

std::vector<std::string> ctuInfoFiles;
for (const auto &f: files) {
const std::string &dumpFileName = getDumpFileName(mSettings, f.path());
const std::string &dumpFileName = getDumpFileName(mSettings, f.path(), 0);
ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName));
}

for (const auto &f: fileSettings) {
const std::string &dumpFileName = getDumpFileName(mSettings, f.filename());
const std::string &dumpFileName = getDumpFileName(mSettings, f.filename(), f.fileIndex);
ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName));
}

Expand Down Expand Up @@ -1944,7 +1945,7 @@ void CppCheck::analyseClangTidy(const FileSettings &fileSettings)
std::string line;

if (!mSettings.buildDir.empty()) {
const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename(), "");
const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename(), "", fileSettings.fileIndex);
std::ofstream fcmd(analyzerInfoFile + ".clang-tidy-cmd");
fcmd << istr.str();
}
Expand Down Expand Up @@ -2036,14 +2037,11 @@ unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const st
std::ifstream fin(filesTxt);
std::string filesTxtLine;
while (std::getline(fin, filesTxtLine)) {
const std::string::size_type firstColon = filesTxtLine.find(':');
if (firstColon == std::string::npos)
continue;
const std::string::size_type lastColon = filesTxtLine.rfind(':');
if (firstColon == lastColon)
AnalyzerInformation::Info filesTxtInfo;
if (!filesTxtInfo.parse(filesTxtLine))
continue;
const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon);
//const std::string sourcefile = filesTxtLine.substr(lastColon+1);

const std::string xmlfile = buildDir + '/' + filesTxtInfo.afile;

tinyxml2::XMLDocument doc;
const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str());
Expand All @@ -2068,7 +2066,7 @@ unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const st
for (const Check *check : Check::instances()) {
if (checkClassAttr == check->name()) {
Check::FileInfo* fi = check->loadFileInfoFromXml(e);
fi->file0 = filesTxtLine.substr(firstColon + 2);
fi->file0 = filesTxtInfo.sourceFile;
fileInfoList.push_back(fi);
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/cppcheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ class CPPCHECKLIB CppCheck {
* @param fileStream stream the file content can be read from
* @return number of errors found
*/
unsigned int checkFile(const FileWithDetails& file, const std::string &cfgname, std::istream* fileStream = nullptr);
unsigned int checkFile(const FileWithDetails& file, const std::string &cfgname, int fileIndex, std::istream* fileStream = nullptr);

/**
* @brief Check normal tokens
Expand Down Expand Up @@ -198,7 +198,7 @@ class CPPCHECKLIB CppCheck {
void executeRules(const std::string &tokenlist, const TokenList &list);
#endif

unsigned int checkClang(const FileWithDetails &file);
unsigned int checkClang(const FileWithDetails &file, int fileIndex);

const Settings& mSettings;
Suppressions& mSuppressions;
Expand Down
Loading
Loading