text: // Copyright 2013 The Goma Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  
  #include <list>
  #include <memory>
  #include <string>
  #include <vector>
  
  #include <glog/stl_logging.h>
  #include <gtest/gtest.h>
  
  #include "absl/strings/match.h"
  #include "absl/strings/str_cat.h"
  #include "absl/strings/str_join.h"
  #include "absl/time/time.h"
  #include "compiler_specific.h"
  #include "cpp_directive_parser.h"
  #include "cpp_parser.h"
  #include "cpp_tokenizer.h"
  #include "cxx/cxx_compiler_info.h"
  #include "include_guard_detector.h"
  
  namespace devtools_goma {
  
  class CppIncludeObserver : public CppParser::IncludeObserver {
   public:
    explicit CppIncludeObserver(CppParser* parser)
        : parser_(parser) {
    }
    ~CppIncludeObserver() override {}
    bool HandleInclude(const std::string& path,
                       const std::string& current_directory ALLOW_UNUSED,
                       const std::string& current_filepath ALLOW_UNUSED,
                       char quote_char ALLOW_UNUSED,
                       int include_dir_index ALLOW_UNUSED) override {
      if (parser_->IsProcessedFile(path, include_dir_index)) {
        ++skipped_[path];
        return true;
      }
      std::map<std::string, std::string>::const_iterator p = includes_.find(path);
      if (p == includes_.end()) {
        return false;
      }
  
      ++included_[path];
  
      SharedCppDirectives directives =
          CppDirectiveParser().ParseFromString(p->second, path);
      std::string include_guard_ident = IncludeGuardDetector::Detect(*directives);
  
      parser_->AddFileInput(IncludeItem(directives, include_guard_ident),
                            p->first,
                            ".",
                            CppParser::kCurrentDirIncludeDirIndex);
      return true;
    }
  
    bool HasInclude(const std::string& path,
                    const std::string& current_directory ALLOW_UNUSED,
                    const std::string& current_filepath ALLOW_UNUSED,
                    char quote_char ALLOW_UNUSED,
                    int include_dir_index ALLOW_UNUSED) override {
      return includes_.find(path) != includes_.end();
    }
  
    void SetInclude(const std::string& filepath, const std::string& content) {
      includes_.insert(make_pair(filepath, content));
    }
  
    int SkipCount(const std::string& filepath) const {
      const auto& it = skipped_.find(filepath);
      if (it == skipped_.end())
        return 0;
      return it->second;
    }
  
    int IncludedCount(const std::string& filepath) const {
      const auto& it = included_.find(filepath);
      if (it == included_.end())
        return 0;
      return it->second;
    }
  
   private:
    CppParser* parser_;
    std::map<std::string, std::string> includes_;
    std::map<std::string, int> skipped_;
    std::map<std::string, int> included_;
    DISALLOW_COPY_AND_ASSIGN(CppIncludeObserver);
  };
  
  class CppErrorObserver : public CppParser::ErrorObserver {
   public:
    CppErrorObserver() {}
    ~CppErrorObserver() override {}
    void HandleError(const std::string& error) override {
      errors_.push_back(error);
    }
    const std::vector<std::string>& errors() const { return errors_; }
  
   private:
    std::vector<std::string> errors_;
    DISALLOW_COPY_AND_ASSIGN(CppErrorObserver);
  };
  
  TEST(CppParserTest, DontCrashWithEmptyInclude) {
    CppParser cpp_parser;
    cpp_parser.AddStringInput("#include\n", "(string)");
    CppErrorObserver err_observer;
    cpp_parser.set_error_observer(&err_observer);
    cpp_parser.ProcessDirectives();
    ASSERT_EQ(1U, err_observer.errors().size());
    EXPECT_EQ("CppParser((string):2) "
              "#include expects \"filename\" or <filename>",
              err_observer.errors()[0]);
  }
  
  TEST(CppParserTest, DontCrashWithEmptyHasInclude) {
    CppParser cpp_parser;
    ASSERT_TRUE(cpp_parser.EnablePredefinedMacro("__has_include", false));
    cpp_parser.AddStringInput(
        "#if __has_include()\n#endif\n"
        "#if __has_include(\n#endif\n"
        "#if __has_include\n",
        "(string)");
    CppErrorObserver err_observer;
    cpp_parser.set_error_observer(&err_observer);
    cpp_parser.ProcessDirectives();
    ASSERT_EQ(3U, err_observer.errors().size());
    EXPECT_TRUE(
        absl::StartsWith(err_observer.errors()[0], "CppParser((string):2) "));
    EXPECT_TRUE(
        absl::StartsWith(err_observer.errors()[1], "CppParser((string):4) "));
    EXPECT_TRUE(
        absl::StartsWith(err_observer.errors()[2], "CppParser((string):6) "));
  }
  
  TEST(CppParserTest, HasFeatureResultValue) {
    std::unique_ptr<CompilerInfoData> info_data(new CompilerInfoData);
    info_data->mutable_cxx()->add_supported_predefined_macros("__has_feature");
    info_data->mutable_cxx()->add_supported_predefined_macros("__has_extension");
    info_data->mutable_cxx()->add_supported_predefined_macros("__has_attribute");
    info_data->mutable_cxx()->add_supported_predefined_macros(
        "__has_cpp_attribute");
    info_data->mutable_cxx()->add_supported_predefined_macros(
        "__has_declspec_attribute");
    info_data->mutable_cxx()->add_supported_predefined_macros("__has_builtin");
    info_data->mutable_cxx()->add_supported_predefined_macros("__has_warning");
    CxxCompilerInfoData::MacroValue* m;
    m = info_data->mutable_cxx()->add_has_feature();
    m->set_key("feature");
    m->set_value(2);
    m = info_data->mutable_cxx()->add_has_extension();
    m->set_key("extension");
    m->set_value(3);
    m = info_data->mutable_cxx()->add_has_attribute();
    m->set_key("attribute");
    m->set_value(4);
    m = info_data->mutable_cxx()->add_has_cpp_attribute();
    m->set_key("cpp_attribute");
    m->set_value(5);
    m = info_data->mutable_cxx()->add_has_declspec_attribute();
    m->set_key("declspec_attribute");
    m->set_value(6);
    m = info_data->mutable_cxx()->add_has_builtin();
    m->set_key("builtin");
    m->set_value(7);
    m = info_data->mutable_cxx()->add_has_warning();
    m->set_key("warning");
    m->set_value(8);
  
    CxxCompilerInfo info(std::move(info_data));
  
    CppParser cpp_parser;
    cpp_parser.SetCompilerInfo(&info);
  
    cpp_parser.AddStringInput(
        "#if __has_feature(feature) == 2\n"
        "# define FEATURE_FEATURE_OK\n"
        "#endif\n"
        "#if __has_feature( feature ) == 2\n"
        "# define FEATURE_FEATURE_SPACE_OK\n"
        "#endif\n"
        "#if __has_feature(extension) == 0\n"
        "# define FEATURE_EXTENSION_OK\n"
        "#endif\n"
        "#if __has_feature(attribute) == 0\n"
        "# define FEATURE_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_feature(cpp_attribute) == 0\n"
        "# define FEATURE_CPP_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_feature(declspec_attribute) == 0\n"
        "# define FEATURE_DECLSPEC_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_feature(builtin) == 0\n"
        "# define FEATURE_BUILTIN_OK\n"
        "#endif\n"
        "#if __has_feature(warning) == 0\n"
        "# define FEATURE_WARNING_OK\n"
        "#endif\n"
        "#if __has_extension(feature) == 2\n"
        "# define EXTENSION_FEATURE_OK\n"
        "#endif\n"
        "#if __has_extension(extension) == 3\n"
        "# define EXTENSION_EXTENSION_OK\n"
        "#endif\n"
        "#if __has_extension( extension ) == 3\n"
        "# define EXTENSION_EXTENSION_SPACE_OK\n"
        "#endif\n"
        "#if __has_extension(attribute) == 0\n"
        "# define EXTENSION_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_extension(cpp_attribute) == 0\n"
        "# define EXTENSION_CPP_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_extension(declspec_attribute) == 0\n"
        "# define EXTENSION_DECLSPEC_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_extension(builtin) == 0\n"
        "# define EXTENSION_BUILTIN_OK\n"
        "#endif\n"
        "#if __has_extension(warning) == 0\n"
        "# define EXTENSION_WARNING_OK\n"
        "#endif\n"
        "#if __has_attribute(feature) == 0\n"
        "# define ATTRIBUTE_FEATURE_OK\n"
        "#endif\n"
        "#if __has_attribute(extension) == 0\n"
        "# define ATTRIBUTE_EXTENSION_OK\n"
        "#endif\n"
        "#if __has_attribute(attribute) == 4\n"
        "# define ATTRIBUTE_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_attribute( attribute ) == 4\n"
        "# define ATTRIBUTE_ATTRIBUTE_SPACE_OK\n"
        "#endif\n"
        "#if __has_attribute(cpp_attribute) == 0\n"
        "# define ATTRIBUTE_CPP_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_attribute(declspec_attribute) == 0\n"
        "# define ATTRIBUTE_DECLSPEC_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_attribute(builtin) == 0\n"
        "# define ATTRIBUTE_BUILTIN_OK\n"
        "#endif\n"
        "#if __has_attribute(warning) == 0\n"
        "# define ATTRIBUTE_WARNING_OK\n"
        "#endif\n"
        "#if __has_cpp_attribute(feature) == 0\n"
        "# define CPP_ATTRIBUTE_FEATURE_OK\n"
        "#endif\n"
        "#if __has_cpp_attribute(extension) == 0\n"
        "# define CPP_ATTRIBUTE_EXTENSION_OK\n"
        "#endif\n"
        "#if __has_cpp_attribute(attribute) == 0\n"
        "# define CPP_ATTRIBUTE_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_cpp_attribute(cpp_attribute) == 5\n"
        "# define CPP_ATTRIBUTE_CPP_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_cpp_attribute( cpp_attribute ) == 5\n"
        "# define CPP_ATTRIBUTE_CPP_ATTRIBUTE_SPACE_OK\n"
        "#endif\n"
        "#if __has_cpp_attribute(declspec_attribute) == 0\n"
        "# define CPP_ATTRIBUTE_DECLSPEC_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_cpp_attribute(builtin) == 0\n"
        "# define CPP_ATTRIBUTE_BUILTIN_OK\n"
        "#endif\n"
        "#if __has_cpp_attribute(warning) == 0\n"
        "# define CPP_ATTRIBUTE_WARNING_OK\n"
        "#endif\n"
        "#if __has_declspec_attribute(feature) == 0\n"
        "# define DECLSPEC_ATTRIBUTE_FEATURE_OK\n"
        "#endif\n"
        "#if __has_declspec_attribute(extension) == 0\n"
        "# define DECLSPEC_ATTRIBUTE_EXTENSION_OK\n"
        "#endif\n"
        "#if __has_declspec_attribute(attribute) == 0\n"
        "# define DECLSPEC_ATTRIBUTE_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_declspec_attribute(cpp_attribute) == 0\n"
        "# define DECLSPEC_ATTRIBUTE_CPP_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_declspec_attribute(declspec_attribute) == 6\n"
        "# define DECLSPEC_ATTRIBUTE_DECLSPEC_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_declspec_attribute( declspec_attribute ) == 6\n"
        "# define DECLSPEC_ATTRIBUTE_DECLSPEC_ATTRIBUTE_SPACE_OK\n"
        "#endif\n"
        "#if __has_declspec_attribute(builtin) == 0\n"
        "# define DECLSPEC_ATTRIBUTE_BUILTIN_OK\n"
        "#endif\n"
        "#if __has_declspec_attribute(warning) == 0\n"
        "# define DECLSPEC_ATTRIBUTE_WARNING_OK\n"
        "#endif\n"
        "#if __has_builtin(feature) == 0\n"
        "# define BUILTIN_FEATURE_OK\n"
        "#endif\n"
        "#if __has_builtin(extension) == 0\n"
        "# define BUILTIN_EXTENSION_OK\n"
        "#endif\n"
        "#if __has_builtin(attribute) == 0\n"
        "# define BUILTIN_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_builtin(cpp_attribute) == 0\n"
        "# define BUILTIN_CPP_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_builtin(declspec_attribute) == 0\n"
        "# define BUILTIN_DECLSPEC_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_builtin(builtin) == 7\n"
        "# define BUILTIN_BUILTIN_OK\n"
        "#endif\n"
        "#if __has_builtin( builtin ) == 7\n"
        "# define BUILTIN_BUILTIN_SPACE_OK\n"
        "#endif\n"
        "#if __has_builtin(warning) == 0\n"
        "# define BUILTIN_WARNING_OK\n"
        "#endif\n"
        "#if __has_warning(feature) == 0\n"
        "# define WARNING_FEATURE_OK\n"
        "#endif\n"
        "#if __has_warning(extension) == 0\n"
        "# define WARNING_EXTENSION_OK\n"
        "#endif\n"
        "#if __has_warning(attribute) == 0\n"
        "# define WARNING_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_warning(cpp_attribute) == 0\n"
        "# define WARNING_CPP_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_warning(declspec_attribute) == 0\n"
        "# define WARNING_DECLSPEC_ATTRIBUTE_OK\n"
        "#endif\n"
        "#if __has_warning(builtin) == 0\n"
        "# define WARNING_BUILTIN_OK\n"
        "#endif\n"
        "#if __has_warning(warning) == 8\n"
        "# define WARNING_WARNING_OK\n"
        "#endif\n"
        "#if __has_warning( warning ) == 8\n"
        "# define WARNING_WARNING_SPACE_OK\n"
        "#endif\n",
        "(string)");
    cpp_parser.ProcessDirectives();
  
    EXPECT_TRUE(cpp_parser.IsMacroDefined("FEATURE_FEATURE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("FEATURE_FEATURE_SPACE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("FEATURE_EXTENSION_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("FEATURE_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("FEATURE_CPP_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("FEATURE_DECLSPEC_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("FEATURE_BUILTIN_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("FEATURE_WARNING_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("EXTENSION_FEATURE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("EXTENSION_EXTENSION_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("EXTENSION_EXTENSION_SPACE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("EXTENSION_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("EXTENSION_CPP_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("EXTENSION_DECLSPEC_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("EXTENSION_BUILTIN_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("EXTENSION_WARNING_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("ATTRIBUTE_FEATURE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("ATTRIBUTE_EXTENSION_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("ATTRIBUTE_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("ATTRIBUTE_ATTRIBUTE_SPACE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("ATTRIBUTE_CPP_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("ATTRIBUTE_DECLSPEC_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("ATTRIBUTE_BUILTIN_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("ATTRIBUTE_WARNING_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("CPP_ATTRIBUTE_FEATURE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("CPP_ATTRIBUTE_EXTENSION_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("CPP_ATTRIBUTE_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("CPP_ATTRIBUTE_CPP_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined(
        "CPP_ATTRIBUTE_CPP_ATTRIBUTE_SPACE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("CPP_ATTRIBUTE_DECLSPEC_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("CPP_ATTRIBUTE_BUILTIN_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("CPP_ATTRIBUTE_WARNING_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("DECLSPEC_ATTRIBUTE_FEATURE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("DECLSPEC_ATTRIBUTE_EXTENSION_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("DECLSPEC_ATTRIBUTE_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("DECLSPEC_ATTRIBUTE_CPP_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined(
                    "DECLSPEC_ATTRIBUTE_DECLSPEC_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined(
                    "DECLSPEC_ATTRIBUTE_DECLSPEC_ATTRIBUTE_SPACE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("DECLSPEC_ATTRIBUTE_BUILTIN_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("DECLSPEC_ATTRIBUTE_WARNING_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("BUILTIN_FEATURE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("BUILTIN_EXTENSION_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("BUILTIN_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("BUILTIN_CPP_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("BUILTIN_DECLSPEC_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("BUILTIN_BUILTIN_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("BUILTIN_BUILTIN_SPACE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("BUILTIN_WARNING_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("WARNING_FEATURE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("WARNING_EXTENSION_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("WARNING_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("WARNING_CPP_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("WARNING_DECLSPEC_ATTRIBUTE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("WARNING_WARNING_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("WARNING_WARNING_SPACE_OK"));
  }
  
  TEST(CppParserTest, ClangExtendedCheckMacro) {
    std::unique_ptr<CompilerInfoData> info_data(new CompilerInfoData);
    info_data->mutable_cxx()->add_supported_predefined_macros(
        "__has_cpp_attribute");
  
    CxxCompilerInfoData::MacroValue* m;
    m = info_data->mutable_cxx()->add_has_cpp_attribute();
    m->set_key("clang::fallthrough");
    m->set_value(1);
  
    CxxCompilerInfo info(std::move(info_data));
  
    CppParser cpp_parser;
    cpp_parser.SetCompilerInfo(&info);
  
    // clang::fallthrough must be allowed.
    cpp_parser.AddStringInput("#if __has_cpp_attribute(clang::fallthrough)\n"
                              "# define FOO\n"
                              "#endif\n"
                              "#if __has_cpp_attribute(clang@@fallthrough)\n"
                              "# define BAR\n"
                              "#endif\n"
                              "#if __has_cpp_attribute(clang::fallthrough)\n"
                              "# define BAZ\n"
                              "#endif\n",
                              "(string)");
  
    CppErrorObserver err_observer;
    cpp_parser.set_error_observer(&err_observer);
    cpp_parser.ProcessDirectives();
  
    EXPECT_TRUE(cpp_parser.IsMacroDefined("FOO"));
    EXPECT_FALSE(cpp_parser.IsMacroDefined("BAR"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("BAZ"));
  
    // TODO: I feel this is a change detection test...
    ASSERT_EQ(1U, err_observer.errors().size()) << err_observer.errors();
    EXPECT_EQ("CppParser((string):5) "
              "__has_cpp_attribute expects an identifier",
              err_observer.errors()[0]);
  }
  
  TEST(CppParserTest, DontCrashWithEmptyTokenInCheckMacro) {
    std::unique_ptr<CompilerInfoData> info_data(new CompilerInfoData);
    info_data->mutable_cxx()->add_supported_predefined_macros("__has_feature");
    info_data->mutable_cxx()->add_supported_predefined_macros("__has_extension");
    info_data->mutable_cxx()->add_supported_predefined_macros("__has_attribute");
    info_data->mutable_cxx()->add_supported_predefined_macros(
        "__has_cpp_attribute");
    info_data->mutable_cxx()->add_supported_predefined_macros(
        "__has_declspec_attribute");
    info_data->mutable_cxx()->add_supported_predefined_macros("__has_builtin");
    info_data->mutable_cxx()->add_supported_predefined_macros("__has_warning");
    CxxCompilerInfoData::MacroValue* m;
    m = info_data->mutable_cxx()->add_has_feature();
    m->set_key("foo");
    m->set_value(1);
    m = info_data->mutable_cxx()->add_has_extension();
    m->set_key("foo");
    m->set_value(1);
    m = info_data->mutable_cxx()->add_has_attribute();
    m->set_key("foo");
    m->set_value(1);
    m = info_data->mutable_cxx()->add_has_cpp_attribute();
    m->set_key("foo");
    m->set_value(1);
    m = info_data->mutable_cxx()->add_has_declspec_attribute();
    m->set_key("foo");
    m->set_value(1);
    m = info_data->mutable_cxx()->add_has_builtin();
    m->set_key("foo");
    m->set_value(1);
    m = info_data->mutable_cxx()->add_has_warning();
    m->set_key("foo");
    m->set_value(1);
  
    CxxCompilerInfo info(std::move(info_data));
  
    CppParser cpp_parser;
    cpp_parser.SetCompilerInfo(&info);
  
    cpp_parser.AddStringInput(
        "#if __has_feature()\n#endif\n"
        "#if __has_feature(\n#endif\n"
        "#if __has_feature\n#endif\n"
        "#if __has_extension()\n#endif\n"
        "#if __has_extension(\n#endif\n"
        "#if __has_extension\n#endif\n"
        "#if __has_attribute()\n#endif\n"
        "#if __has_attribute(\n#endif\n"
        "#if __has_attribute\n#endif\n"
        "#if __has_cpp_attribute()\n#endif\n"
        "#if __has_cpp_attribute(\n#endif\n"
        "#if __has_cpp_attribute\n#endif\n"
        "#if __has_declspec_attribute()\n#endif\n"
        "#if __has_declspec_attribute(\n#endif\n"
        "#if __has_declspec_attribute\n#endif\n"
        "#if __has_builtin()\n#endif\n"
        "#if __has_builtin(\n#endif\n"
        "#if __has_builtin\n#endif\n"
        "#if __has_warning()\n#endif\n"
        "#if __has_warning(\n#endif\n"
        "#if __has_warning\n#endif\n",
        "(string)");
    CppErrorObserver err_observer;
    cpp_parser.set_error_observer(&err_observer);
    cpp_parser.ProcessDirectives();
    constexpr unsigned kNumExpectedErrors = 21U;
    ASSERT_EQ(kNumExpectedErrors, err_observer.errors().size())
        << err_observer.errors();
    for (int i = 0; i < kNumExpectedErrors; ++i) {
      std::string error_prefix(
          absl::StrCat("CppParser((string):", (i + 1) * 2, ") "));
      EXPECT_TRUE(absl::StartsWith(err_observer.errors()[i], error_prefix))
          << "error=" << err_observer.errors()[i]
          << " expect prefix=" << error_prefix;
    }
  }
  
  TEST(CppParserTest, ExpandMacro) {
    CppParser cpp_parser;
    cpp_parser.AddStringInput("#define M() 1\n"
                              "#if M()\n"
                              "#endif\n"
                              "#if M(x)\n"
                              "#endif\n"
                              "#define M1(x) x\n"
                              "#if M1()\n"
                              "#endif\n"
                              "#if M1(1)\n"
                              "#endif\n"
                              "#define M2(x,y) x+y\n"
                              "#if M2(1,1)\n"
                              "#endif\n"
                              "#if M2(,1)\n"
                              "#endif\n"
                              "#if M2(1,)\n"
                              "#endif\n"
                              "#if M2()\n"
                              "#endif\n"
                              "#if M2(1)\n"
                              "#endif\n"
                              "#if M2(1,,1)\n"
                              "#endif\n",
                              "(string)");
    CppErrorObserver err_observer;
    cpp_parser.set_error_observer(&err_observer);
    cpp_parser.ProcessDirectives();
    ASSERT_EQ(4U, err_observer.errors().size())
        << absl::StrJoin(err_observer.errors(), "\n");
    // TODO: line number is #endif line that just after #if that
    // error happened?
    EXPECT_EQ("CppParser((string):5) "  // M(x)
              "macro argument number mismatching with the parameter list",
              err_observer.errors()[0]);
    EXPECT_EQ("CppParser((string):19) "  // M2()
              "macro argument number mismatching with the parameter list",
              err_observer.errors()[1]);
    EXPECT_EQ("CppParser((string):21) "  // M2(1)
              "macro argument number mismatching with the parameter list",
              err_observer.errors()[2]);
    EXPECT_EQ("CppParser((string):23) "  // M2(1,,1)
              "macro argument number mismatching with the parameter list",
              err_observer.errors()[3]);
  }
  
  TEST(CppParserTest, IncludeMoreThanOnce) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
    include_observer.SetInclude("foo.h",
                                "#ifdef hoge\n"
                                "#endif\n");
    cpp_parser.set_include_observer(&include_observer);
    cpp_parser.AddStringInput("#define hoge\n"
                              "#include <foo.h>\n"
                              "#undef hoge\n"
                              "#include <foo.h>\n",
                              "foo.cc");
    cpp_parser.ProcessDirectives();
    EXPECT_EQ(2, cpp_parser.total_files());
    EXPECT_EQ(0, cpp_parser.skipped_files());
  }
  
  TEST(CppParserTest, ImportOnlyOnce) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
    include_observer.SetInclude("foo.h",
                                "#ifdef hoge\n"
                                "#endif\n");
    cpp_parser.set_include_observer(&include_observer);
    cpp_parser.AddStringInput("#define hoge\n"
                              "#import <foo.h>\n"
                              "#undef hoge\n"
                              "#import <foo.h>\n",
                              "foo.cc");
    cpp_parser.ProcessDirectives();
    EXPECT_EQ(2, cpp_parser.total_files());
    EXPECT_EQ(1, cpp_parser.skipped_files());
  }
  
  TEST(CppParserTest, BoolShouldBeTreatedAsBoolOnCplusplus) {
    CppParser cpp_parser;
    cpp_parser.set_is_cplusplus(true);
    cpp_parser.AddStringInput("#if true\n"
                              "#define foo\n"
                              "#endif\n"
                              "#if false\n"
                              "#define bar\n"
                              "#endif\n",
                              "baz.cc");
    cpp_parser.ProcessDirectives();
    EXPECT_TRUE(cpp_parser.IsMacroDefined("foo"));
    EXPECT_FALSE(cpp_parser.IsMacroDefined("bar"));
  }
  
  TEST(CppParserTest, BoolShouldNotBeTreatedAsBoolOnNonCplusplus) {
    CppParser cpp_parser;
    cpp_parser.AddStringInput("#if true\n"
                              "#define foo\n"
                              "#endif\n"
                              "#if false\n"
                              "#define bar\n"
                              "#endif\n",
                              "baz.cc");
    cpp_parser.ProcessDirectives();
    EXPECT_FALSE(cpp_parser.IsMacroDefined("foo"));
    EXPECT_FALSE(cpp_parser.IsMacroDefined("bar"));
  }
  
  TEST(CppParserTest, BoolShouldNotBeTreatedAsDefined) {
    CppParser cpp_parser;
    cpp_parser.set_is_cplusplus(true);
    cpp_parser.AddStringInput("#if true\n"
                              "#define foo\n"
                              "#endif\n"
                              "#if defined(true)\n"
                              "#define bar\n"
                              "#endif\n"
                              "#if false\n"
                              "#define baz\n"
                              "#endif\n"
                              "#if defined(false)\n"
                              "#define qux\n"
                              "#endif\n",
                              "baz.cc");
    cpp_parser.ProcessDirectives();
    EXPECT_TRUE(cpp_parser.IsMacroDefined("foo"));
    EXPECT_FALSE(cpp_parser.IsMacroDefined("bar"));
    EXPECT_FALSE(cpp_parser.IsMacroDefined("baz"));
    EXPECT_FALSE(cpp_parser.IsMacroDefined("qux"));
  }
  
  TEST(CppParserTest, BoolShouldBeOverriddenByMacroInTrueToTrueCase) {
    CppParser cpp_parser;
    cpp_parser.set_is_cplusplus(true);
    cpp_parser.AddStringInput("#define true true\n"
                              "#if true\n"
                              "#define foo\n"
                              "#endif\n"
                              "#if defined(true)\n"
                              "#define bar\n"
                              "#endif\n",
                              "baz.cc");
    cpp_parser.ProcessDirectives();
    EXPECT_TRUE(cpp_parser.IsMacroDefined("foo"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("bar"));
  }
  
  TEST(CppParserTest, BoolShouldBeOverriddenByMacroInTrueToFalseCase) {
    CppParser cpp_parser;
    cpp_parser.set_is_cplusplus(true);
    cpp_parser.AddStringInput("#define true false\n"
                              "#if true\n"
                              "#define foo\n"
                              "#endif\n"
                              "#if defined(true)\n"
                              "#define bar\n"
                              "#endif\n",
                              "baz.cc");
    cpp_parser.ProcessDirectives();
    EXPECT_FALSE(cpp_parser.IsMacroDefined("foo"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("bar"));
  }
  
  TEST(CppParserTest, BoolShouldBeOverriddenByMacroInFalseToTrueCase) {
    CppParser cpp_parser;
    cpp_parser.set_is_cplusplus(true);
    cpp_parser.AddStringInput("#define false true\n"
                              "#if false\n"
                              "#define foo\n"
                              "#endif\n"
                              "#if defined(false)\n"
                              "#define bar\n"
                              "#endif\n",
                              "baz.cc");
    cpp_parser.ProcessDirectives();
    EXPECT_TRUE(cpp_parser.IsMacroDefined("foo"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("bar"));
  }
  
  TEST(CppParserTest, BoolShouldBeOverriddenByMacroInFalseToFalseCase) {
    CppParser cpp_parser;
    cpp_parser.set_is_cplusplus(true);
    cpp_parser.AddStringInput("#define false false\n"
                              "#if false\n"
                              "#define foo\n"
                              "#endif\n"
                              "#if defined(false)\n"
                              "#define bar\n"
                              "#endif\n",
                              "baz.cc");
    cpp_parser.ProcessDirectives();
    EXPECT_FALSE(cpp_parser.IsMacroDefined("foo"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("bar"));
  }
  
  TEST(CppParserTest, BoolShouldBeOverriddenAndPossibleToUndefOnTrueCase) {
    CppParser cpp_parser;
    cpp_parser.set_is_cplusplus(true);
    cpp_parser.AddStringInput("#define true false\n"
                              "#if true\n"
                              "#define foo\n"
                              "#endif\n"
                              "#undef true\n"
                              "#if true\n"
                              "#define bar\n"
                              "#endif\n",
                              "baz.cc");
    cpp_parser.ProcessDirectives();
    EXPECT_FALSE(cpp_parser.IsMacroDefined("foo"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("bar"));
  }
  
  TEST(CppParserTest, BoolShouldBeOverriddenAndPossibleToUndefOnFalseCase) {
    CppParser cpp_parser;
    cpp_parser.set_is_cplusplus(true);
    cpp_parser.AddStringInput("#define false true\n"
                              "#if false\n"
                              "#define foo\n"
                              "#endif\n"
                              "#undef false\n"
                              "#if false\n"
                              "#define bar\n"
                              "#endif\n",
                              "baz.cc");
    cpp_parser.ProcessDirectives();
    EXPECT_TRUE(cpp_parser.IsMacroDefined("foo"));
    EXPECT_FALSE(cpp_parser.IsMacroDefined("bar"));
  }
  
  TEST(CppParserTest, CharToken) {
    CppParser cpp_parser;
  
    // non-ASCII system is not supported.
    cpp_parser.AddStringInput("#if 'A' == 65\n"
                              "#define foo_true\n"
                              "#else\n"
                              "#define foo_false\n"
                              "#endif\n"
  
                              "#if 39 == '\\''\n"
                              "#define bar_true\n"
                              "#else\n"
                              "#define bar_false\n"
                              "#endif\n"
  
                              "#if '*' == 42\n"
                              "#define OPERATOR_OK\n"
                              "#endif\n"
  
                              "#if '0' == 48\n"
                              "#define DIGIT_OK\n"
                              "#endif\n"
  
                              "#if ' ' == 32 && ' ' == 0x20 && ' ' == 040\n"
                              "#define SPACE_OK\n"
                              "#endif\n"
  
                              "#if '\\0' == 0\n"
                              "#define ZERO_OK\n"
                              "#endif\n"
  
                              "#if '\\n' == 10\n"
                              "#define LF_OK\n"
                              "#endif\n"
  
                              // macro in lua's lctype.h
                              "#if 'A' == 65 && '0' == 48\n"
                              "#define LUA_USE_CTYPE 0\n"
                              "#else\n"
                              "#define LUA_USE_CTYPE 1\n"
                              "#endif\n"
  
                              "#if !LUA_USE_CTYPE\n"
                              "#define INCLUDE_LLIMITS\n"
                              "#endif\n",
  
                              "baz.cc");
    cpp_parser.ProcessDirectives();
  
    EXPECT_TRUE(cpp_parser.IsMacroDefined("foo_true"));
    EXPECT_FALSE(cpp_parser.IsMacroDefined("foo_false"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("bar_true"));
    EXPECT_FALSE(cpp_parser.IsMacroDefined("bar_false"));
  
    EXPECT_TRUE(cpp_parser.IsMacroDefined("OPERATOR_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("DIGIT_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("SPACE_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("ZERO_OK"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("LF_OK"));
  
    EXPECT_TRUE(cpp_parser.IsMacroDefined("INCLUDE_LLIMITS"));
  }
  
  TEST(CppParserTest, DateTimeToken) {
    CppParser cpp_parser;
  
    // We care that the date/time string format is valid. The value is not
    // important.
    absl::Time dummy_time;
    EXPECT_TRUE(absl::ParseTime("%b %d %Y", cpp_parser.GetDate().string_value,
                &dummy_time, nullptr));
    EXPECT_TRUE(absl::ParseTime("%H:%M:%S", cpp_parser.GetTime().string_value,
                &dummy_time, nullptr));
  }
  
  TEST(CppParserTest, MacroSetChanged) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
    include_observer.SetInclude("a.h",
                                "#ifndef A_H\n"
                                "#define A_H\n"
                                "#endif\n"
                                "#undef X\n");
  
    include_observer.SetInclude("b.h",
                                "#ifndef B_H\n"
                                "#define B_H\n"
                                "#define X 1\n"
                                "#include \"a.h\"\n"
                                "#define Y 1\n"
                                "#endif\n");
  
    cpp_parser.set_include_observer(&include_observer);
    cpp_parser.AddStringInput("#include \"a.h\"\n"
                              "#include \"b.h\"\n",
                              "a.cc");
    cpp_parser.ProcessDirectives();
  
    // After #include "a.h" in b.h, X must be undefined.
    // Including a.h should not be skipped.
    EXPECT_FALSE(cpp_parser.IsMacroDefined("X"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("Y"));
  }
  
  TEST(CppParserTest, TopFileMacroDefinitionUpdate) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
    include_observer.SetInclude("a.h",
                                "#ifdef INCLUDE_B\n"
                                "#include \"b.h\"\n"
                                "#endif\n");
    include_observer.SetInclude("b.h",
                                "#define B\n");
    include_observer.SetInclude("c.h",
                                "#include \"a.h\"\n");
  
    cpp_parser.set_include_observer(&include_observer);
    cpp_parser.AddStringInput("#include \"a.h\"\n"
                              "#define INCLUDE_B\n"
                              "#include \"c.h\"\n",
                              "a.cc");
    cpp_parser.ProcessDirectives();
  
    // After #define INCLUDE_B in a.cc, the result of
    // #ifdef INCLUDE_B in a.h should be changed.
    EXPECT_TRUE(cpp_parser.IsMacroDefined("B"));
  }
  
  TEST(CppParserTest, SkippedByIncludeGuard) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
  
    include_observer.SetInclude("a.h",
                                "#ifndef A_H\n"
                                "#define A_H\n"
                                "#endif");
    include_observer.SetInclude("b.h",
                                "#ifndef B_H\n"
                                "#define B_H\n"
                                "#include \"a.h\"\n"
                                "#endif");
    include_observer.SetInclude("c.h",
                                "#ifndef C_H\n"
                                "#define C_H\n"
                                "#include \"b.h\"\n"
                                "#endif");
  
    cpp_parser.set_include_observer(&include_observer);
  
    cpp_parser.AddStringInput(
      "#include \"c.h\"\n"
      "#include \"b.h\"\n"
      "#include \"a.h\"\n", "(string)");
    cpp_parser.ProcessDirectives();
  
    EXPECT_EQ(1, include_observer.IncludedCount("a.h"));
    EXPECT_EQ(1, include_observer.IncludedCount("b.h"));
    EXPECT_EQ(1, include_observer.IncludedCount("c.h"));
  
    EXPECT_EQ(1, include_observer.SkipCount("a.h"));
    EXPECT_EQ(1, include_observer.SkipCount("b.h"));
    EXPECT_EQ(0, include_observer.SkipCount("c.h"));
  }
  
  TEST(CppParserTest, SkippedByIncludeGuardIfDefinedCase) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
  
    include_observer.SetInclude("a.h",
                                "#if !defined(A_H)\n"
                                "#define A_H\n"
                                "#endif");
    include_observer.SetInclude("b.h",
                                "#if !defined(B_H)\n"
                                "#define B_H\n"
                                "#include \"a.h\"\n"
                                "#endif");
    include_observer.SetInclude("c.h",
                                "#if !defined(C_H)\n"
                                "#define C_H\n"
                                "#include \"b.h\"\n"
                                "#endif");
  
    cpp_parser.set_include_observer(&include_observer);
  
    cpp_parser.AddStringInput(
      "#include \"c.h\"\n"
      "#include \"b.h\"\n"
      "#include \"a.h\"\n", "(string)");
    cpp_parser.ProcessDirectives();
  
    EXPECT_EQ(1, include_observer.IncludedCount("a.h"));
    EXPECT_EQ(1, include_observer.IncludedCount("b.h"));
    EXPECT_EQ(1, include_observer.IncludedCount("c.h"));
  
    EXPECT_EQ(1, include_observer.SkipCount("a.h"));
    EXPECT_EQ(1, include_observer.SkipCount("b.h"));
    EXPECT_EQ(0, include_observer.SkipCount("c.h"));
  }
  
  TEST(CppParserTest, SkippedByIncludeGuardIfDefinedInvalidCase) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
  
    // Only a.h is the correct include guard.
    // So, we won't skip the other header files.
  
    include_observer.SetInclude("a.h",
                                "#if !defined(A_H)\n"
                                "#define A_H\n"
                                "#endif");
  
    include_observer.SetInclude("b.h",
                                "#if !defined(B_H) || 1\n"
                                "#define B_H\n"
                                "#endif");
  
    include_observer.SetInclude("c.h",
                                "#if 1 || !defined(C_H)\n"
                                "#define C_H\n"
                                "#endif");
  
    include_observer.SetInclude("d.h",
                                "#if ID(!defined(D_H))\n"
                                "#define D_H\n"
                                "#endif");
  
    include_observer.SetInclude("e.h",
                                "#if defined(E_H)\n"
                                "#define E_H\n"
                                "#endif");
  
    cpp_parser.set_include_observer(&include_observer);
  
    cpp_parser.AddStringInput(
      "#define ID(X) X\n"
      "#include \"a.h\"\n"
      "#include \"a.h\"\n"
      "#include \"b.h\"\n"
      "#include \"b.h\"\n"
      "#include \"c.h\"\n"
      "#include \"c.h\"\n"
      "#include \"d.h\"\n"
      "#include \"d.h\"\n"
      "#include \"e.h\"\n"
      "#include \"e.h\"\n", "(string)");
    cpp_parser.ProcessDirectives();
  
    EXPECT_EQ(1, include_observer.IncludedCount("a.h"));
    EXPECT_EQ(2, include_observer.IncludedCount("b.h"));
    EXPECT_EQ(2, include_observer.IncludedCount("c.h"));
    EXPECT_EQ(2, include_observer.IncludedCount("d.h"));
    EXPECT_EQ(2, include_observer.IncludedCount("e.h"));
  
    EXPECT_EQ(1, include_observer.SkipCount("a.h"));
    EXPECT_EQ(0, include_observer.SkipCount("b.h"));
    EXPECT_EQ(0, include_observer.SkipCount("c.h"));
    EXPECT_EQ(0, include_observer.SkipCount("d.h"));
    EXPECT_EQ(0, include_observer.SkipCount("e.h"));
  }
  
  TEST(CppParserTest, DontSkipdByIncludeGuardIfndefButNotDefined) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
  
    include_observer.SetInclude("a.h",
                                "#ifndef FOO\n"
                                "# include \"b.h\"\n"
                                "#else\n"
                                "# include \"c.h\"\n"
                                "#endif\n");
    include_observer.SetInclude("b.h",
                                "#define B_H");
    include_observer.SetInclude("c.h",
                                "#define C_H");
  
    cpp_parser.set_include_observer(&include_observer);
  
    cpp_parser.AddStringInput(
        "#include \"a.h\"\n"
        "#define FOO\n"
        "#include \"a.h\"\n", "(string)");
    cpp_parser.ProcessDirectives();
  
    EXPECT_EQ(2, include_observer.IncludedCount("a.h"));
    EXPECT_EQ(1, include_observer.IncludedCount("b.h"));
    EXPECT_EQ(1, include_observer.IncludedCount("c.h"));
  
    EXPECT_EQ(0, include_observer.SkipCount("a.h"));
    EXPECT_EQ(0, include_observer.SkipCount("b.h"));
    EXPECT_EQ(0, include_observer.SkipCount("c.h"));
  }
  
  TEST(CppParserTest, DontSkipdIncludeGuardAndUndefined) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
  
    include_observer.SetInclude("a.h",
                                "#ifndef FOO\n"
                                "#define FOO\n"
                                "#endif\n");
  
    cpp_parser.set_include_observer(&include_observer);
  
    cpp_parser.AddStringInput(
        "#include \"a.h\"\n"
        "#undef FOO\n"
        "#include \"a.h\"\n", "(string)");
    cpp_parser.ProcessDirectives();
  
    EXPECT_EQ(2, include_observer.IncludedCount("a.h"));
  
    EXPECT_EQ(0, include_observer.SkipCount("a.h"));
  }
  
  TEST(CppParserTest, ColonPercentShouldBeTreatedAsSharp) {
    CppParser cpp_parser;
    cpp_parser.AddStringInput("#define  a  b  %:%: c \n"
                              "#define bc 1\n"
                              "#if a == bc\n"
                              "#define correct\n"
                              "#else\n"
                              "#define wrong\n"
                              "#endif\n",
                              "(string)");
    cpp_parser.ProcessDirectives();
    EXPECT_TRUE(cpp_parser.IsMacroDefined("a"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("correct"));
    EXPECT_FALSE(cpp_parser.IsMacroDefined("wrong"));
  }
  
  TEST(CppParserTest, SpaceInMacroShouldBeTreatedAsIs) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
  
    include_observer.SetInclude("foobar",
                                "");
    include_observer.SetInclude("foo bar",
                                "");
    include_observer.SetInclude("foo  bar",
                                "");
  
    cpp_parser.set_include_observer(&include_observer);
  
    // FOO2 is expanded to <foo_bar>, not <foo__bar> (underscore means a space)
    cpp_parser.AddStringInput("#define FOO1 <foo bar>\n"
                              "#define FOO2 <foo  bar>\n"
                              "#include FOO1\n"
                              "#include FOO2\n",
                              "foo.cc");
    cpp_parser.ProcessDirectives();
  
    EXPECT_EQ(0, include_observer.IncludedCount("foobar"));
    EXPECT_EQ(2, include_observer.IncludedCount("foo bar"));
    EXPECT_EQ(0, include_observer.IncludedCount("foo  bar"));
  }
  
  TEST(CppParserTest, SpaceNearDoubleSharpShouldBeTreatedCorrectly) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
  
    include_observer.SetInclude("hogefuga",
                                "");
  
    cpp_parser.set_include_observer(&include_observer);
  
    cpp_parser.AddStringInput("#define cut(x, y) <x   ##   y>\n"
                              "#include cut(hoge, fuga)\n",
                              "foo.cc");
    cpp_parser.ProcessDirectives();
  
    EXPECT_EQ(1, include_observer.IncludedCount("hogefuga"));
  }
  
  TEST(CppParserTest, DirectiveWithSpaces) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
  
    include_observer.SetInclude("x.h", "");
    include_observer.SetInclude("y.h", "");
  
    cpp_parser.set_include_observer(&include_observer);
  
    cpp_parser.AddStringInput("\n"
                              " # define foo\n"
                              "  #   define bar\n"
                              " # ifdef foo\n"
                              "  #  include \"x.h\"\n"
                              " # endif\n"
                              "# ifdef bar\n"
                              "# include \"y.h\"\n",
                              "# endif\n"
                              "foo.cc");
    cpp_parser.ProcessDirectives();
  
    EXPECT_EQ(1, include_observer.IncludedCount("x.h"));
    EXPECT_EQ(1, include_observer.IncludedCount("y.h"));
  }
  
  TEST(CppParserTest, MultiAddMacroByString) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
  
    include_observer.SetInclude("x.h", "");
    include_observer.SetInclude("y.h", "");
  
    cpp_parser.set_include_observer(&include_observer);
  
    cpp_parser.AddMacroByString("macro1", "");
    cpp_parser.AddMacroByString("macro2", "");
    cpp_parser.AddStringInput("#ifdef macro1\n"
                              "#include \"x.h\"\n"
                              "#endif\n"
                              "#ifdef macro2\n"
                              "#include \"y.h\"\n"
                              "#endif\n",
                              "foo.cc");
    cpp_parser.ProcessDirectives();
  
    EXPECT_TRUE(cpp_parser.IsMacroDefined("macro1"));
    EXPECT_TRUE(cpp_parser.IsMacroDefined("macro2"));
  
    EXPECT_EQ(1, include_observer.IncludedCount("x.h"));
    EXPECT_EQ(1, include_observer.IncludedCount("y.h"));
  }
  
  TEST(CppParserTest, LimitIncludeDepth) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
  
    include_observer.SetInclude("bar.h",
                                "#include \"bar.h\"\n");
  
    cpp_parser.set_include_observer(&include_observer);
  
    cpp_parser.AddStringInput("#include \"bar.h\"\n",
                              "foo.cc");
    EXPECT_FALSE(cpp_parser.ProcessDirectives());
    EXPECT_EQ(1024, include_observer.IncludedCount("bar.h"));
  }
  
  // Regression test from android source (b/78436008)
  TEST(CppParserTest, GluingInteger) {
    CppParser cpp_parser;
  
    cpp_parser.AddStringInput(
        "#define _WIN32_WINNT 0x0600\n"
        "#define NV_FROM_WIN32_WINNT2(V) V##0000\n"
        "#define NV_FROM_WIN32_WINNT(V) NV_FROM_WIN32_WINNT2(V)\n"
        "#define NV NV_FROM_WIN32_WINNT(_WIN32_WINNT)\n"
        "#if NV >= 0x06000000\n"
        "# define OK 1\n"
        "#endif\n",
        "foo.cc");
  
    EXPECT_TRUE(cpp_parser.ProcessDirectives());
    EXPECT_TRUE(cpp_parser.IsMacroDefined("OK"));
  }
  
  // regression test for b/30547167
  TEST(CppParserTest, MissingHeader) {
    CppParser cpp_parser;
    CppIncludeObserver include_observer(&cpp_parser);
    cpp_parser.set_include_observer(&include_observer);
  
    cpp_parser.AddStringInput("#include <should_not_exist.h>", "foo.cc");
  
    EXPECT_FALSE(cpp_parser.ProcessDirectives());
  }
  
  // regression test for b/167131595
  TEST(CppParserTest, ObjectiveCXXIsCPlusPlus) {
    CppParser cpp_parser;
    std::unique_ptr<CompilerInfoData> info_data =
        absl::make_unique<CompilerInfoData>();
    info_data->set_lang("objective-c++");
    info_data->mutable_cxx();
    CxxCompilerInfo compiler_info(std::move(info_data));
    cpp_parser.SetCompilerInfo(&compiler_info);
  
    CppIncludeObserver include_observer(&cpp_parser);
    cpp_parser.set_include_observer(&include_observer);
  
    cpp_parser.AddStringInput(
        "#define DCHECK_IS_ON() true\n"
        "#if DCHECK_IS_ON()\n"
        "# define OK 1\n"
        "#endif\n",
        "foo.cc");
  
    EXPECT_TRUE(cpp_parser.ProcessDirectives());
    EXPECT_TRUE(cpp_parser.IsMacroDefined("OK"));
  }
  
  }  // namespace devtools_goma
  