From 799b1aad7dfe1c0555a089d80c5f9926fdf8a912 Mon Sep 17 00:00:00 2001 From: firewave Date: Wed, 24 Jan 2024 09:21:21 +0100 Subject: [PATCH] added command-line option `--cpp-header-probe` to probe headers and extension-less files for Emacs C++ marker [skip ci] --- cli/cmdlineparser.cpp | 8 ++ lib/cppcheck.cpp | 6 +- lib/path.cpp | 101 +++++++++++++++++++++++- lib/path.h | 3 +- lib/preprocessor.cpp | 2 +- lib/settings.h | 3 + lib/tokenlist.cpp | 2 +- releasenotes.txt | 1 + test/cli/other_test.py | 28 +++++++ test/cli/testutils.py | 4 +- test/testcmdlineparser.cpp | 32 ++++++++ test/testpath.cpp | 153 ++++++++++++++++++++++++++++--------- 12 files changed, 293 insertions(+), 50 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 4f2209e11f65..cb3570cbb5ab 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -544,6 +544,10 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a } } + else if (std::strcmp(argv[i], "--cpp-header-probe") == 0) { + mSettings.cppHeaderProbe = true; + } + // Show --debug output after the first simplifications else if (std::strcmp(argv[i], "--debug") == 0 || std::strcmp(argv[i], "--debug-normal") == 0) @@ -887,6 +891,10 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a return Result::Fail; } + else if (std::strcmp(argv[i], "--no-cpp-header-probe") == 0) { + mSettings.cppHeaderProbe = false; + } + // Write results in file else if (std::strncmp(argv[i], "--output-file=", 14) == 0) mSettings.outputFile = Path::simplifyPath(Path::fromNativeSeparators(argv[i] + 14)); diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 60eb88951d2d..c5d65ca68c78 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -182,7 +182,7 @@ static void createDumpFile(const Settings& settings, case Standards::Language::None: { // TODO: error out on unknown language? - const Standards::Language lang = Path::identify(filename); + const Standards::Language lang = Path::identify(filename, settings.cppHeaderProbe); if (lang == Standards::Language::CPP) language = " language=\"cpp\""; else if (lang == Standards::Language::C) @@ -420,7 +420,7 @@ unsigned int CppCheck::checkClang(const std::string &path) mErrorLogger.reportOut(std::string("Checking ") + path + " ...", Color::FgGreen); // TODO: this ignores the configured language - const bool isCpp = Path::identify(path) == Standards::Language::CPP; + const bool isCpp = Path::identify(path, mSettings.cppHeaderProbe) == Standards::Language::CPP; const std::string langOpt = isCpp ? "-x c++" : "-x c"; const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, emptyString); const std::string clangcmd = analyzerInfo + ".clang-cmd"; @@ -783,7 +783,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string TokenList tokenlist(&mSettings); std::istringstream istr2(code); // TODO: asserts when file has unknown extension - tokenlist.createTokens(istr2, Path::identify(*files.begin())); // TODO: check result? + tokenlist.createTokens(istr2, Path::identify(*files.begin(), false)); // TODO: check result? executeRules("define", tokenlist); } #endif diff --git a/lib/path.cpp b/lib/path.cpp index f7d698034629..a65557f4fccb 100644 --- a/lib/path.cpp +++ b/lib/path.cpp @@ -20,11 +20,17 @@ #undef __STRICT_ANSI__ #endif +#define LOG_EMACS_MARKER + #include "path.h" #include "utils.h" #include +#include #include +#ifdef LOG_EMACS_MARKER +#include +#endif #include #include #include @@ -235,7 +241,7 @@ bool Path::isCPP(const std::string &path) bool Path::acceptFile(const std::string &path, const std::set &extra) { bool header = false; - return (identify(path, &header) != Standards::Language::None && !header) || extra.find(getFilenameExtension(path)) != extra.end(); + return (identify(path, false, &header) != Standards::Language::None && !header) || extra.find(getFilenameExtension(path)) != extra.end(); } // cppcheck-suppress unusedFunction @@ -245,13 +251,98 @@ bool Path::isHeader(const std::string &path) return startsWith(extension, ".h"); } -Standards::Language Path::identify(const std::string &path, bool *header) +static bool hasEmacsCppMarker(const char* path) +{ + // TODO: identify is called three times for each file + // Preprocessor::loadFiles() -> createDUI() + // Preprocessor::preprocess() -> createDUI() + // TokenList::createTokens() -> TokenList::determineCppC() +#ifdef LOG_EMACS_MARKER + std::cout << path << '\n'; +#endif + + FILE *fp = fopen(path, "rt"); + if (!fp) + return false; + std::string buf(128, '\0'); + { + // TODO: read the whole first line only + const char * const res = fgets(const_cast(buf.data()), buf.size(), fp); + fclose(fp); + fp = nullptr; + if (!res) + return false; // failed to read file + } + // TODO: replace with regular expression + const auto pos1 = buf.find("-*-"); + if (pos1 == std::string::npos) + return false; // no start marker + const auto pos_nl = buf.find_first_of("\r\n"); + if (pos_nl != std::string::npos && (pos_nl < pos1)) { +#ifdef LOG_EMACS_MARKER + std::cout << path << " - Emacs marker not on the first line" << '\n'; +#endif + return false; // not on first line + } + const auto pos2 = buf.find("-*-", pos1 + 3); + // TODO: make sure we have read the whole line before bailing out + if (pos2 == std::string::npos) { +#ifdef LOG_EMACS_MARKER + std::cout << path << " - Emacs marker not terminated" << '\n'; +#endif + return false; // no end marker + } +#ifdef LOG_EMACS_MARKER + std::cout << "Emacs marker: '" << buf.substr(pos1, (pos2 + 3) - pos1) << "'" << '\n'; +#endif + const std::string buf_trim = trim(buf); // trim whitespaces + if (buf_trim[0] != '/' || buf_trim[1] != '/') { +#ifdef LOG_EMACS_MARKER + std::cout << path << " - Emacs marker not in a comment: '" << buf.substr(pos1, (pos2 + 3) - pos1) << "'" << '\n'; +#endif + return false; // not a comment + } + + // there are more variations with lowercase and no whitespaces + // -*- C++ -*- + // -*- Mode: C++; -*- + // -*- Mode: C++; c-basic-offset: 8 -*- + std::string marker = trim(buf.substr(pos1 + 3, pos2 - pos1 - 3), " ;"); + // cut off additional attributes + const auto pos_semi = marker.find(';'); + if (pos_semi != std::string::npos) + marker.resize(pos_semi); + findAndReplace(marker, "mode:", ""); + findAndReplace(marker, "Mode:", ""); + marker = trim(marker); + if (marker == "C++" || marker == "c++") + return true; // C++ marker found + + //if (marker == "C" || marker == "c") + // return false; +#ifdef LOG_EMACS_MARKER + std::cout << path << " - unmatched Emacs marker: '" << marker << "'" << '\n'; +#endif + + return false; // marker is not a C++ one +} + +Standards::Language Path::identify(const std::string &path, bool cppHeaderProbe, bool *header) { // cppcheck-suppress uninitvar - TODO: FP if (header) *header = false; std::string ext = getFilenameExtension(path); + // standard library headers have no extension + if (cppHeaderProbe && ext.empty()) { + if (hasEmacsCppMarker(path.c_str())) { + if (header) + *header = true; + return Standards::Language::CPP; + } + return Standards::Language::None; + } if (ext == ".C") return Standards::Language::CPP; if (c_src_exts.find(ext) != c_src_exts.end()) @@ -262,7 +353,9 @@ Standards::Language Path::identify(const std::string &path, bool *header) if (ext == ".h") { if (header) *header = true; - return Standards::Language::C; // treat as C for now + if (cppHeaderProbe && hasEmacsCppMarker(path.c_str())) + return Standards::Language::CPP; + return Standards::Language::C; } if (cpp_src_exts.find(ext) != cpp_src_exts.end()) return Standards::Language::CPP; @@ -277,7 +370,7 @@ Standards::Language Path::identify(const std::string &path, bool *header) bool Path::isHeader2(const std::string &path) { bool header; - (void)Path::identify(path, &header); + (void)identify(path, false, &header); return header; } diff --git a/lib/path.h b/lib/path.h index 9e0e733046cc..3015c8da8899 100644 --- a/lib/path.h +++ b/lib/path.h @@ -187,10 +187,11 @@ class CPPCHECKLIB Path { /** * @brief Identify the language based on the file extension * @param path filename to check. path info is optional + * @param cppHeaderProbe check optional Emacs marker to identify extension-less and *.h files as C++ * @param header if provided indicates if the file is a header * @return the language type */ - static Standards::Language identify(const std::string &path, bool *header = nullptr); + static Standards::Language identify(const std::string &path, bool cppHeaderProbe, bool *header = nullptr); /** * @brief Get filename without a directory path part. diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 70e2e560770c..bbd0f67ea573 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -684,7 +684,7 @@ static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cf dui.includes = mSettings.userIncludes; // --include // TODO: use mSettings.standards.stdValue instead // TODO: error out on unknown language? - const Standards::Language lang = Path::identify(filename); + const Standards::Language lang = Path::identify(filename, mSettings.cppHeaderProbe); if (lang == Standards::Language::CPP) { dui.std = mSettings.standards.getCPP(); splitcfg(mSettings.platform.getLimitsDefines(Standards::getCPP(dui.std)), dui.defines, ""); diff --git a/lib/settings.h b/lib/settings.h index 6cbceeefbaf7..2ec4df0453a0 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -167,6 +167,9 @@ class CPPCHECKLIB WARN_UNUSED Settings { /** cppcheck.cfg: About text */ std::string cppcheckCfgAbout; + /** @brief check Emacs marker to detect extension-less and *.h files as C++ */ + bool cppHeaderProbe{}; + /** @brief Are we running from DACA script? */ bool daca{}; diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 786647aa4ab7..8f368b3157ff 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -96,7 +96,7 @@ void TokenList::determineCppC() // only try to determine if it wasn't enforced if (mLang == Standards::Language::None) { ASSERT_LANG(!getSourceFilePath().empty()); - mLang = Path::identify(getSourceFilePath()); + mLang = Path::identify(getSourceFilePath(), mSettings ? mSettings->cppHeaderProbe : false); // TODO: cannot enable assert as this might occur for unknown extensions //ASSERT_LANG(mLang != Standards::Language::None); if (mLang == Standards::Language::None) { diff --git a/releasenotes.txt b/releasenotes.txt index a04d884e540b..f992096acd85 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -16,4 +16,5 @@ Deprecations: - Other: +- added command-line option `--cpp-header-probe` (and `--no-cpp-header-probe`) to probe headers and extension-less files for Emacs marker (see https://trac.cppcheck.net/ticket/10692 for more details) - diff --git a/test/cli/other_test.py b/test/cli/other_test.py index ec3d15c7c686..a922d0f4576f 100644 --- a/test/cli/other_test.py +++ b/test/cli/other_test.py @@ -1428,3 +1428,31 @@ def test_filelist(tmpdir): for i in range(1, len(expected)+1): lines.remove('{}/11 files checked 0% done'.format(i, len(expected))) assert lines == expected + + +def test_cpp_probe(tmpdir): + test_file = os.path.join(tmpdir, 'test.h') + with open(test_file, 'wt') as f: + f.writelines([ + 'class A {};' + ]) + + args = ['-q', '--template=simple', '--cpp-header-probe', test_file] + err_lines = [ + "{}:1:1: error: Code 'classA{{' is invalid C code. Use --std or --language to configure the language. [syntaxError]".format(test_file) + ] + + assert_cppcheck(args, ec_exp=0, err_exp=err_lines, out_exp=[]) + + +def test_cpp_probe_2(tmpdir): + test_file = os.path.join(tmpdir, 'test.h') + with open(test_file, 'wt') as f: + f.writelines([ + '// -*- C++ -*-', + 'class A {};' + ]) + + args = ['-q', '--template=simple', '--cpp-header-probe', test_file] + + assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=[]) diff --git a/test/cli/testutils.py b/test/cli/testutils.py index a6692bde8bc5..8f1bde408708 100644 --- a/test/cli/testutils.py +++ b/test/cli/testutils.py @@ -163,7 +163,7 @@ def assert_cppcheck(args, ec_exp=None, out_exp=None, err_exp=None, env=None): assert exitcode == ec_exp, stdout if out_exp is not None: out_lines = stdout.splitlines() - assert out_lines == out_exp, stdout + assert out_lines == out_exp, out_lines if err_exp is not None: err_lines = stderr.splitlines() - assert err_lines == err_exp, stderr + assert err_lines == err_exp, err_lines diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index cde8d2381de6..f544830ba6b0 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -387,6 +387,10 @@ class TestCmdlineParser : public TestFixture { TEST_CASE(checkLevelNormal); TEST_CASE(checkLevelExhaustive); TEST_CASE(checkLevelUnknown); + TEST_CASE(cppHeaderProbe); + TEST_CASE(cppHeaderProbe2); + TEST_CASE(noCppHeaderProbe); + TEST_CASE(noCppHeaderProbe2); TEST_CASE(ignorepaths1); TEST_CASE(ignorepaths2); @@ -2603,6 +2607,34 @@ class TestCmdlineParser : public TestFixture { ASSERT_EQUALS("cppcheck: error: unknown '--check-level' value 'default'.\n", logger->str()); } + void cppHeaderProbe() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--cpp-header-probe", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->cppHeaderProbe); + } + + void cppHeaderProbe2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--no-cpp-header-probe", "--cpp-header-probe", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS(true, settings->cppHeaderProbe); + } + + void noCppHeaderProbe() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--no-cpp-header-probe", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(false, settings->cppHeaderProbe); + } + + void noCppHeaderProbe2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--cpp-header-probe", "--no-cpp-header-probe", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS(false, settings->cppHeaderProbe); + } + void ignorepaths1() { REDIRECT; const char * const argv[] = {"cppcheck", "-isrc", "file.cpp"}; diff --git a/test/testpath.cpp b/test/testpath.cpp index 0bd3ae2f5f92..2b60a1800022 100644 --- a/test/testpath.cpp +++ b/test/testpath.cpp @@ -48,6 +48,7 @@ class TestPath : public TestFixture { TEST_CASE(sameFileName); TEST_CASE(getFilenameExtension); TEST_CASE(identify); + TEST_CASE(identifyWithCppProbe); TEST_CASE(is_header_2); } @@ -292,80 +293,156 @@ class TestPath : public TestFixture { Standards::Language lang; bool header; - ASSERT_EQUALS(Standards::Language::None, Path::identify("")); - ASSERT_EQUALS(Standards::Language::None, Path::identify("c")); - ASSERT_EQUALS(Standards::Language::None, Path::identify("cpp")); - ASSERT_EQUALS(Standards::Language::None, Path::identify("h")); - ASSERT_EQUALS(Standards::Language::None, Path::identify("hpp")); + ASSERT_EQUALS(Standards::Language::None, Path::identify("", false)); + ASSERT_EQUALS(Standards::Language::None, Path::identify("c", false)); + ASSERT_EQUALS(Standards::Language::None, Path::identify("cpp", false)); + ASSERT_EQUALS(Standards::Language::None, Path::identify("h", false)); + ASSERT_EQUALS(Standards::Language::None, Path::identify("hpp", false)); // TODO: what about files starting with a "."? - //ASSERT_EQUALS(Standards::Language::None, Path::identify(".c")); - //ASSERT_EQUALS(Standards::Language::None, Path::identify(".cpp")); - //ASSERT_EQUALS(Standards::Language::None, Path::identify(".h")); - //ASSERT_EQUALS(Standards::Language::None, Path::identify(".hpp")); + //ASSERT_EQUALS(Standards::Language::None, Path::identify(".c", false)); + //ASSERT_EQUALS(Standards::Language::None, Path::identify(".cpp", false)); + //ASSERT_EQUALS(Standards::Language::None, Path::identify(".h", false)); + //ASSERT_EQUALS(Standards::Language::None, Path::identify(".hpp", false)); // C - ASSERT_EQUALS(Standards::Language::C, Path::identify("index.c")); - ASSERT_EQUALS(Standards::Language::C, Path::identify("index.cl")); - ASSERT_EQUALS(Standards::Language::C, Path::identify("C:\\foo\\index.c")); - ASSERT_EQUALS(Standards::Language::C, Path::identify("/mnt/c/foo/index.c")); + ASSERT_EQUALS(Standards::Language::C, Path::identify("index.c", false)); + ASSERT_EQUALS(Standards::Language::C, Path::identify("index.cl", false)); + ASSERT_EQUALS(Standards::Language::C, Path::identify("C:\\foo\\index.c", false)); + ASSERT_EQUALS(Standards::Language::C, Path::identify("/mnt/c/foo/index.c", false)); // In unix .C is considered C++ #ifdef _WIN32 - ASSERT_EQUALS(Standards::Language::C, Path::identify("C:\\foo\\index.C")); + ASSERT_EQUALS(Standards::Language::C, Path::identify("C:\\foo\\index.C", false)); #endif - lang = Path::identify("index.c", &header); + lang = Path::identify("index.c", false, &header); ASSERT_EQUALS(Standards::Language::C, lang); ASSERT_EQUALS(false, header); // C++ - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.cpp")); - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.cxx")); - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.cc")); - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.c++")); - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.tpp")); - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.txx")); - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.ipp")); - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.ixx")); - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("C:\\foo\\index.cpp")); - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("C:\\foo\\index.Cpp")); - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("/mnt/c/foo/index.cpp")); - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("/mnt/c/foo/index.Cpp")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.cpp", false)); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.cxx", false)); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.cc", false)); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.c++", false)); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.tpp", false)); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.txx", false)); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.ipp", false)); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.ixx", false)); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("C:\\foo\\index.cpp", false)); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("C:\\foo\\index.Cpp", false)); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("/mnt/c/foo/index.cpp", false)); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("/mnt/c/foo/index.Cpp", false)); // TODO: check for case-insenstive filesystem instead // In unix .C is considered C++ #if !defined(_WIN32) && !(defined(__APPLE__) && defined(__MACH__)) - ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.C")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.C", false)); #else - ASSERT_EQUALS(Standards::Language::C, Path::identify("index.C")); + ASSERT_EQUALS(Standards::Language::C, Path::identify("index.C", false)); #endif - lang = Path::identify("index.cpp", &header); + lang = Path::identify("index.cpp", false, &header); ASSERT_EQUALS(Standards::Language::CPP, lang); ASSERT_EQUALS(false, header); // headers - lang = Path::identify("index.h", &header); + lang = Path::identify("index.h", false, &header); ASSERT_EQUALS(Standards::Language::C, lang); ASSERT_EQUALS(true, header); - lang = Path::identify("index.hpp", &header); + lang = Path::identify("index.hpp", false, &header); ASSERT_EQUALS(Standards::Language::CPP, lang); ASSERT_EQUALS(true, header); - lang = Path::identify("index.hxx", &header); + lang = Path::identify("index.hxx", false, &header); ASSERT_EQUALS(Standards::Language::CPP, lang); ASSERT_EQUALS(true, header); - lang = Path::identify("index.h++", &header); + lang = Path::identify("index.h++", false, &header); ASSERT_EQUALS(Standards::Language::CPP, lang); ASSERT_EQUALS(true, header); - lang = Path::identify("index.hh", &header); + lang = Path::identify("index.hh", false, &header); ASSERT_EQUALS(Standards::Language::CPP, lang); ASSERT_EQUALS(true, header); - ASSERT_EQUALS(Standards::Language::None, Path::identify("index.header")); - ASSERT_EQUALS(Standards::Language::None, Path::identify("index.htm")); - ASSERT_EQUALS(Standards::Language::None, Path::identify("index.html")); + ASSERT_EQUALS(Standards::Language::None, Path::identify("index.header", false)); + ASSERT_EQUALS(Standards::Language::None, Path::identify("index.htm", false)); + ASSERT_EQUALS(Standards::Language::None, Path::identify("index.html", false)); + } + + // TODO: file, line + void identifyWithCppProbeInternal(const std::string& filename, const std::string& marker, Standards::Language std) const + { + const ScopedFile file(filename, marker); + ASSERT_EQUALS_MSG(std, Path::identify(file.path(), true), marker); + } + + void identifyWithCppProbe() const + { + // TODO: are these valid? + // // -*- C++ --*- + // // -*- C++/-*- + // /* -*- C++ -*- +/ + + const std::list files = { + "cppprobe.h", + "cppprobe" + }; + + const std::list markers_cpp = { + "// -*- C++ -*-", + "// -*-C++-*-", + "// -*- Mode: C++; -*-", + "// -*-Mode: C++;-*-", + "// -*- Mode:C++; -*-", + "// -*-Mode:C++;-*-", + "// -*- Mode: C++ -*-", + "// -*-Mode: C++-*-", + "// -*- Mode:C++ -*-", + "// -*-Mode:C++-*-", + "// -*- Mode: C++; c-basic-offset: 8 -*-", + "// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-", + + "// -*- c++ -*-", + "// -*- mode: c++; -*-", + + "//-*- C++ -*-", + " //-*- C++ -*-", + "\t//-*- C++ -*-", + "\t //-*- C++ -*-", + " \t//-*- C++ -*-", + "// -----*- C++ -*-----", + "// comment-*- C++ -*-comment", + "//-*- C++ -*-\r// comment", + "//-*- C++ -*-\n// comment", + "//-*- C++ -*-\r\n// comment" + }; + + for (const auto& f : { "cppprobe.h", "cppprobe" }) { + for (const auto& m : markers_cpp) { + identifyWithCppProbeInternal(f, m, Standards::Language::CPP); + } + } + + const std::list markers_c = { + "-*- C++ -*-", // needs to be in comment + "// -*-C", // no end marker + "// comment\n// -*-C", // not on the first line + "// comment\r// -*-C", // not on the first line + "// comment\r\n// -*-C", // not on the first line + "// -*- C -*-", + "// -*- Mode: C; -*-", + "// -*- f90 -*-", + "// -*- fortran -*-", + "// -*- c-basic-offset: 2 -*-", + "// -*- c-basic-offset:4; indent-tabs-mode:nil -*-" + }; + + for (const auto& m : markers_c) { + identifyWithCppProbeInternal("cppprobe.h", m, Standards::Language::C); + } + for (const auto& m : markers_c) { + identifyWithCppProbeInternal("cppprobe", m, Standards::Language::None); + } } void is_header_2() const {