blob: 1022ad2098c897fe73c6e191d7e46955e6a6c8b9 [file] [log] [blame]
// 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 string& path,
const string& current_directory ALLOW_UNUSED,
const 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<string, string>::const_iterator p = includes_.find(path);
if (p == includes_.end()) {
return false;
}
++included_[path];
SharedCppDirectives directives =
CppDirectiveParser().ParseFromString(p->second, path);
string include_guard_ident = IncludeGuardDetector::Detect(*directives);
parser_->AddFileInput(IncludeItem(directives, include_guard_ident),
p->first,
".",
CppParser::kCurrentDirIncludeDirIndex);
return true;
}
bool HasInclude(
const string& path,
const string& current_directory ALLOW_UNUSED,
const 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 string& filepath, const string& content) {
includes_.insert(make_pair(filepath, content));
}
int SkipCount(const string& filepath) const {
const auto& it = skipped_.find(filepath);
if (it == skipped_.end())
return 0;
return it->second;
}
int IncludedCount(const string& filepath) const {
const auto& it = included_.find(filepath);
if (it == included_.end())
return 0;
return it->second;
}
private:
CppParser* parser_;
std::map<string, string> includes_;
std::map<string, int> skipped_;
std::map<string, int> included_;
DISALLOW_COPY_AND_ASSIGN(CppIncludeObserver);
};
class CppErrorObserver : public CppParser::ErrorObserver {
public:
CppErrorObserver() {}
~CppErrorObserver() override {}
void HandleError(const string& error) override {
errors_.push_back(error);
}
const std::vector<string>& errors() const {
return errors_;
}
private:
std::vector<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");
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);
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_extension(feature) == 0\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_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_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_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_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", "(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("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("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("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("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("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"));
}
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");
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);
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",
"(string)");
CppErrorObserver err_observer;
cpp_parser.set_error_observer(&err_observer);
cpp_parser.ProcessDirectives();
ASSERT_EQ(18U, err_observer.errors().size()) << err_observer.errors();
for (int i = 0; i < 18; ++i) {
string error_prefix(absl::StrCat("CppParser((string):", (i + 1) * 2, ") "));
EXPECT_TRUE(absl::StartsWith(err_observer.errors()[i], 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"));
}
} // namespace devtools_goma