Skip to content
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

store language in FileWithDetails #7116

Merged
merged 1 commit into from
Jan 2, 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
68 changes: 68 additions & 0 deletions cli/cmdlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,39 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[])

mFileSettings.clear();

if (mSettings.enforcedLang != Standards::Language::None)
{
// apply enforced language
for (auto& fs : fileSettings)
{
if (mSettings.library.markupFile(fs.filename()))
continue;
fs.file.setLang(mSettings.enforcedLang);
}
}
else
{
// identify files
for (auto& fs : fileSettings)
{
if (mSettings.library.markupFile(fs.filename()))
continue;
bool header = false;
fs.file.setLang(Path::identify(fs.filename(), mSettings.cppHeaderProbe, &header));
// unknown extensions default to C++
if (!header && fs.file.lang() == Standards::Language::None)
fs.file.setLang(Standards::Language::CPP);
}
}

// enforce the language since markup files are special and do not adhere to the enforced language
for (auto& fs : fileSettings)
{
if (mSettings.library.markupFile(fs.filename())) {
fs.file.setLang(Standards::Language::C);
}
}

// sort the markup last
std::copy_if(fileSettings.cbegin(), fileSettings.cend(), std::back_inserter(mFileSettings), [&](const FileSettings &fs) {
return !mSettings.library.markupFile(fs.filename()) || !mSettings.library.processMarkupAfterCode(fs.filename());
Expand Down Expand Up @@ -284,6 +317,41 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[])
files = std::move(filesResolved);
}

if (mSettings.enforcedLang != Standards::Language::None)
{
// apply enforced language
for (auto& f : files)
{
if (mSettings.library.markupFile(f.path()))
continue;
f.setLang(mSettings.enforcedLang);
}
}
else
{
// identify remaining files
for (auto& f : files)
{
if (f.lang() != Standards::Language::None)
continue;
if (mSettings.library.markupFile(f.path()))
continue;
bool header = false;
f.setLang(Path::identify(f.path(), mSettings.cppHeaderProbe, &header));
// unknown extensions default to C++
if (!header && f.lang() == Standards::Language::None)
f.setLang(Standards::Language::CPP);
}
}

// enforce the language since markup files are special and do not adhere to the enforced language
for (auto& f : files)
{
if (mSettings.library.markupFile(f.path())) {
f.setLang(Standards::Language::C);
}
}

// sort the markup last
std::copy_if(files.cbegin(), files.cend(), std::inserter(mFiles, mFiles.end()), [&](const FileWithDetails& entry) {
return !mSettings.library.markupFile(entry.path()) || !mSettings.library.processMarkupAfterCode(entry.path());
Expand Down
12 changes: 7 additions & 5 deletions cli/filelister.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ static std::string addFiles2(std::list<FileWithDetails>&files, const std::string

if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
// File
if ((!checkAllFilesInDir || Path::acceptFile(fname, extra)) && !ignored.match(fname)) {
Standards::Language lang = Standards::Language::None;
if ((!checkAllFilesInDir || Path::acceptFile(fname, extra, &lang)) && !ignored.match(fname)) {
std::string nativename = Path::fromNativeSeparators(fname);

// Limitation: file sizes are assumed to fit in a 'size_t'
Expand All @@ -114,7 +115,7 @@ static std::string addFiles2(std::list<FileWithDetails>&files, const std::string
const std::size_t filesize = ffd.nFileSizeLow;

#endif
files.emplace_back(std::move(nativename), filesize);
files.emplace_back(std::move(nativename), lang, filesize);
}
} else {
// Directory
Expand Down Expand Up @@ -192,7 +193,7 @@ static std::string addFiles2(std::list<FileWithDetails> &files,
return ""; // TODO: return error?
if ((file_stat.st_mode & S_IFMT) != S_IFDIR)
{
files.emplace_back(path, file_stat.st_size);
files.emplace_back(path, Standards::Language::None, file_stat.st_size);
return "";
}

Expand Down Expand Up @@ -229,12 +230,13 @@ static std::string addFiles2(std::list<FileWithDetails> &files,
}
}
} else {
if (Path::acceptFile(new_path, extra) && !ignored.match(new_path)) {
Standards::Language lang = Standards::Language::None;
if (Path::acceptFile(new_path, extra, &lang) && !ignored.match(new_path)) {
if (stat(new_path.c_str(), &file_stat) == -1) {
const int err = errno;
return "could not stat file '" + new_path + "' (errno: " + std::to_string(err) + ")";
}
files.emplace_back(new_path, file_stat.st_size);
files.emplace_back(new_path, lang, file_stat.st_size);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion gui/checkthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ void CheckThread::run()
qDebug() << "Whole program analysis";
std::list<FileWithDetails> files2;
std::transform(mFiles.cbegin(), mFiles.cend(), std::back_inserter(files2), [&](const QString& file) {
return FileWithDetails{file.toStdString(), 0};
return FileWithDetails{file.toStdString(), Path::identify(file.toStdString(), cppcheck.settings().cppHeaderProbe), 0};
});
cppcheck.analyseWholeProgram(cppcheck.settings().buildDir, files2, {}, ctuInfo);
mFiles.clear();
Expand Down
21 changes: 17 additions & 4 deletions lib/filesettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "config.h"
#include "path.h"
#include "platform.h"
#include "standards.h"

#include <list>
#include <set>
Expand All @@ -33,12 +34,13 @@ class FileWithDetails
{
public:
explicit FileWithDetails(std::string path)
: FileWithDetails(std::move(path), 0)
: FileWithDetails(std::move(path), Standards::Language::None, 0)
{}

FileWithDetails(std::string path, std::size_t size)
FileWithDetails(std::string path, Standards::Language lang, std::size_t size)
: mPath(std::move(path))
, mPathSimplified(Path::simplifyPath(mPath))
, mLang(lang)
, mSize(size)
{
if (mPath.empty())
Expand All @@ -59,9 +61,20 @@ class FileWithDetails
{
return mSize;
}

void setLang(Standards::Language lang)
{
mLang = lang;
}

Standards::Language lang() const
{
return mLang;
}
private:
std::string mPath;
std::string mPathSimplified;
Standards::Language mLang = Standards::Language::None;
std::size_t mSize;
};

Expand All @@ -71,8 +84,8 @@ struct CPPCHECKLIB FileSettings {
: file(std::move(path))
{}

FileSettings(std::string path, std::size_t size)
: file(std::move(path), size)
FileSettings(std::string path, Standards::Language lang, std::size_t size)
: file(std::move(path), lang, size)
{}

std::string cfg;
Expand Down
7 changes: 5 additions & 2 deletions lib/path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,13 @@ static const std::unordered_set<std::string> header_exts = {
".h", ".hpp", ".h++", ".hxx", ".hh"
};

bool Path::acceptFile(const std::string &path, const std::set<std::string> &extra)
bool Path::acceptFile(const std::string &path, const std::set<std::string> &extra, Standards::Language* lang)
{
bool header = false;
return (identify(path, false, &header) != Standards::Language::None && !header) || extra.find(getFilenameExtension(path)) != extra.end();
Standards::Language l = identify(path, false, &header);
if (lang)
*lang = l;
return (l != Standards::Language::None && !header) || extra.find(getFilenameExtension(path)) != extra.end();
}

static bool hasEmacsCppMarker(const char* path)
Expand Down
10 changes: 6 additions & 4 deletions lib/path.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,21 +137,23 @@ class CPPCHECKLIB Path {
* @brief Check if the file extension indicates that it's a C/C++ source file.
* Check if the file has source file extension: *.c;*.cpp;*.cxx;*.c++;*.cc;*.txx
* @param filename filename to check. path info is optional
* @param lang the detected language
* @return true if the file extension indicates it should be checked
*/
static bool acceptFile(const std::string &filename) {
static bool acceptFile(const std::string &filename, Standards::Language* lang = nullptr) {
const std::set<std::string> extra;
return acceptFile(filename, extra);
return acceptFile(filename, extra, lang);
}

/**
* @brief Check if the file extension indicates that it's a C/C++ source file.
* Check if the file has source file extension: *.c;*.cpp;*.cxx;*.c++;*.cc;*.txx
* @param path filename to check. path info is optional
* @param extra extra file extensions
* @param extra extra file extensions
* @param lang the detected language
* @return true if the file extension indicates it should be checked
*/
static bool acceptFile(const std::string &path, const std::set<std::string> &extra);
static bool acceptFile(const std::string &path, const std::set<std::string> &extra, Standards::Language* lang = nullptr);

/**
* @brief Is filename a header based on file extension
Expand Down
19 changes: 17 additions & 2 deletions test/cli/clang-import_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,10 @@ def test_warning(tmpdir): # #12424
assert stderr == ''


def __test_cmd(tmp_path, file_name, extra_args, stdout_exp_1):
def __test_cmd(tmp_path, file_name, extra_args, stdout_exp_1, content=''):
test_file = tmp_path / file_name
with open(test_file, 'wt') as f:
f.write('')
f.write(content)

args = [
'--enable=information',
Expand Down Expand Up @@ -173,6 +173,21 @@ def test_cmd_cpp(tmp_path):
__test_cmd(tmp_path, 'test.cpp', [], '-x c++')


# files with unknown extensions are treated as C++
@pytest.mark.xfail(strict=True)
def test_cmd_unk(tmp_path):
__test_cmd(tmp_path, 'test.cplusplus', [], '-x c++')


# headers are treated as C by default
def test_cmd_hdr(tmp_path):
__test_cmd(tmp_path, 'test.h', [], '-x c')


def test_cmd_hdr_probe(tmp_path):
__test_cmd(tmp_path, 'test.h', ['--cpp-header-probe'], '-x c++', '// -*- C++ -*-')


def test_cmd_inc(tmp_path):
inc_path = tmp_path / 'inc'
os.makedirs(inc_path)
Expand Down
9 changes: 9 additions & 0 deletions test/testcmdlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,8 @@ class TestCmdlineParser : public TestFixture {
TEST_CASE(ignorefilepaths2);
TEST_CASE(ignorefilepaths3);

TEST_CASE(nonexistentpath);

TEST_CASE(checkconfig);
TEST_CASE(unknownParam);

Expand Down Expand Up @@ -2955,6 +2957,13 @@ class TestCmdlineParser : public TestFixture {
ASSERT_EQUALS("foo.cpp", parser->getIgnoredPaths()[0]);
}

void nonexistentpath() {
REDIRECT;
const char * const argv[] = {"cppcheck", "file.cpp"};
ASSERT(!parser->fillSettingsFromArgs(2, argv));
ASSERT_EQUALS("cppcheck: error: could not find or open any of the paths given.\n", logger->str());
}

void checkconfig() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--check-config", "file.cpp"};
Expand Down
83 changes: 78 additions & 5 deletions test/testfilelister.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class TestFileLister : public TestFixture {
TEST_CASE(recursiveAddFilesEmptyPath);
TEST_CASE(excludeFile1);
TEST_CASE(excludeFile2);
TEST_CASE(addFiles);
}

// TODO: generate file list instead
Expand Down Expand Up @@ -81,11 +82,25 @@ class TestFileLister : public TestFixture {
};

// Make sure source files are added..
ASSERT(find_file(dirprefix + "cli/main.cpp") != files.end());
ASSERT(find_file(dirprefix + "lib/token.cpp") != files.end());
ASSERT(find_file(dirprefix + "lib/tokenize.cpp") != files.end());
ASSERT(find_file(dirprefix + "gui/main.cpp") != files.end());
ASSERT(find_file(dirprefix + "test/testfilelister.cpp") != files.end());
auto it = find_file(dirprefix + "cli/main.cpp");
ASSERT(it != files.end());
ASSERT_EQUALS_ENUM(Standards::Language::CPP, it->lang());

it = find_file(dirprefix + "lib/token.cpp");
ASSERT(it != files.end());
ASSERT_EQUALS_ENUM(Standards::Language::CPP, it->lang());

it = find_file(dirprefix + "lib/tokenize.cpp");
ASSERT(it != files.end());
ASSERT_EQUALS_ENUM(Standards::Language::CPP, it->lang());

it = find_file(dirprefix + "gui/main.cpp");
ASSERT(it != files.end());
ASSERT_EQUALS_ENUM(Standards::Language::CPP, it->lang());

it = find_file(dirprefix + "test/testfilelister.cpp");
ASSERT(it != files.end());
ASSERT_EQUALS_ENUM(Standards::Language::CPP, it->lang());

// Make sure headers are not added..
ASSERT(find_file(dirprefix + "lib/tokenize.h") == files.end());
Expand Down Expand Up @@ -120,7 +135,65 @@ class TestFileLister : public TestFixture {
ASSERT_EQUALS(basedir + "lib/token.cpp", files.begin()->path());
}

void addFiles() const {
const std::string adddir = findBaseDir() + ".";

// TODO: on Windows the prefix is different from when a recursive a folder (see recursiveAddFiles test)
const std::string dirprefix = adddir + "/";
#ifdef _WIN32
const std::string dirprefix_nat = Path::toNativeSeparators(dirprefix);
#endif

std::list<FileWithDetails> files;

{
const std::string addfile = Path::join(Path::join(adddir, "cli"), "main.cpp");
const std::string err = FileLister::addFiles(files, addfile, {}, true,PathMatch({}));
ASSERT_EQUALS("", err);
}
{
const std::string addfile = Path::join(Path::join(adddir, "lib"), "token.cpp");
const std::string err = FileLister::addFiles(files, addfile, {}, true,PathMatch({}));
ASSERT_EQUALS("", err);
}
{
const std::string addfile = Path::join(Path::join(adddir, "cli"), "token.cpp"); // does not exist
const std::string err = FileLister::addFiles(files, addfile, {}, true,PathMatch({}));
ASSERT_EQUALS("", err);
}
{
const std::string addfile = Path::join(Path::join(adddir, "lib2"), "token.cpp"); // does not exist
const std::string err = FileLister::addFiles(files, addfile, {}, true,PathMatch({}));
#ifdef _WIN32
// TODO: get rid of this error - caused by missing intermediate folder
ASSERT_EQUALS("finding files failed. Search pattern: '" + dirprefix_nat + "lib2\\token.cpp'. (error: 3)", err);
#else
ASSERT_EQUALS("", err);
#endif
}
{
const std::string addfile = Path::join(Path::join(adddir, "lib"), "matchcompiler.h");
const std::string err = FileLister::addFiles(files, addfile, {}, true,PathMatch({}));
ASSERT_EQUALS("", err);
}

ASSERT_EQUALS(3, files.size());
auto it = files.cbegin();
ASSERT_EQUALS(dirprefix + "cli/main.cpp", it->path());
ASSERT_EQUALS(Path::simplifyPath(dirprefix + "cli/main.cpp"), it->spath());
ASSERT_EQUALS_ENUM(Standards::Language::None, it->lang());
it++;
ASSERT_EQUALS(dirprefix + "lib/token.cpp", it->path());
ASSERT_EQUALS(Path::simplifyPath(dirprefix + "lib/token.cpp"), it->spath());
ASSERT_EQUALS_ENUM(Standards::Language::None, it->lang());
it++;
ASSERT_EQUALS(dirprefix + "lib/matchcompiler.h", it->path());
ASSERT_EQUALS(Path::simplifyPath(dirprefix + "lib/matchcompiler.h"), it->spath());
ASSERT_EQUALS_ENUM(Standards::Language::None, it->lang());
}

// TODO: test errors
// TODO: test wildcards
};

REGISTER_TEST(TestFileLister)
Loading
Loading