llvm-project/clang-tools-extra/pseudo/unittests/DirectiveMapTest.cpp

151 lines
5.0 KiB
C++

//===--- DirectiveMapTest.cpp ---------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang-pseudo/DirectiveMap.h"
#include "clang-pseudo/Token.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/TokenKinds.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace clang {
namespace pseudo {
namespace {
using testing::_;
using testing::ElementsAre;
using testing::Matcher;
using testing::Pair;
using testing::StrEq;
using Chunk = DirectiveMap::Chunk;
MATCHER_P2(tokensAre, TS, Tokens, "tokens are " + std::string(Tokens)) {
std::vector<llvm::StringRef> Texts;
for (const Token &Tok : TS.tokens(arg.Tokens))
Texts.push_back(Tok.text());
return Matcher<std::string>(StrEq(Tokens))
.MatchAndExplain(llvm::join(Texts, " "), result_listener);
}
MATCHER_P(chunkKind, K, "") { return arg.kind() == K; }
TEST(DirectiveMap, Parse) {
LangOptions Opts;
std::string Code = R"cpp(
#include <foo.h>
int main() {
#ifdef HAS_FOO
#if HAS_BAR
foo(bar);
#else
foo(0)
#endif
#elif NEEDS_FOO
#error missing_foo
#endif
}
)cpp";
TokenStream S = cook(lex(Code, Opts), Opts);
DirectiveMap PP = DirectiveMap::parse(S);
ASSERT_THAT(PP.Chunks, ElementsAre(chunkKind(Chunk::K_Directive),
chunkKind(Chunk::K_Code),
chunkKind(Chunk::K_Conditional),
chunkKind(Chunk::K_Code)));
EXPECT_THAT((const DirectiveMap::Directive &)PP.Chunks[0],
tokensAre(S, "# include < foo . h >"));
EXPECT_THAT((const DirectiveMap::Code &)PP.Chunks[1],
tokensAre(S, "int main ( ) {"));
EXPECT_THAT((const DirectiveMap::Code &)PP.Chunks[3], tokensAre(S, "}"));
const DirectiveMap::Conditional &Ifdef(PP.Chunks[2]);
EXPECT_THAT(Ifdef.Branches,
ElementsAre(Pair(tokensAre(S, "# ifdef HAS_FOO"), _),
Pair(tokensAre(S, "# elif NEEDS_FOO"), _)));
EXPECT_THAT(Ifdef.End, tokensAre(S, "# endif"));
const DirectiveMap &HasFoo(Ifdef.Branches[0].second);
const DirectiveMap &NeedsFoo(Ifdef.Branches[1].second);
EXPECT_THAT(HasFoo.Chunks, ElementsAre(chunkKind(Chunk::K_Conditional)));
const DirectiveMap::Conditional &If(HasFoo.Chunks[0]);
EXPECT_THAT(If.Branches, ElementsAre(Pair(tokensAre(S, "# if HAS_BAR"), _),
Pair(tokensAre(S, "# else"), _)));
EXPECT_THAT(If.Branches[0].second.Chunks,
ElementsAre(chunkKind(Chunk::K_Code)));
EXPECT_THAT(If.Branches[1].second.Chunks,
ElementsAre(chunkKind(Chunk::K_Code)));
EXPECT_THAT(NeedsFoo.Chunks, ElementsAre(chunkKind(Chunk::K_Directive)));
const DirectiveMap::Directive &Error(NeedsFoo.Chunks[0]);
EXPECT_THAT(Error, tokensAre(S, "# error missing_foo"));
EXPECT_EQ(Error.Kind, tok::pp_error);
}
TEST(DirectiveMap, ParseUgly) {
LangOptions Opts;
std::string Code = R"cpp(
/*A*/ # /*B*/ \
/*C*/ \
define \
BAR /*D*/
/*E*/
)cpp";
TokenStream S = cook(lex(Code, Opts), Opts);
DirectiveMap PP = DirectiveMap::parse(S);
ASSERT_THAT(PP.Chunks, ElementsAre(chunkKind(Chunk::K_Code),
chunkKind(Chunk::K_Directive),
chunkKind(Chunk::K_Code)));
EXPECT_THAT((const DirectiveMap::Code &)PP.Chunks[0], tokensAre(S, "/*A*/"));
const DirectiveMap::Directive &Define(PP.Chunks[1]);
EXPECT_EQ(Define.Kind, tok::pp_define);
EXPECT_THAT(Define, tokensAre(S, "# /*B*/ /*C*/ define BAR /*D*/"));
EXPECT_THAT((const DirectiveMap::Code &)PP.Chunks[2], tokensAre(S, "/*E*/"));
}
TEST(DirectiveMap, ParseBroken) {
LangOptions Opts;
std::string Code = R"cpp(
a
#endif // mismatched
#if X
b
)cpp";
TokenStream S = cook(lex(Code, Opts), Opts);
DirectiveMap PP = DirectiveMap::parse(S);
ASSERT_THAT(PP.Chunks, ElementsAre(chunkKind(Chunk::K_Code),
chunkKind(Chunk::K_Directive),
chunkKind(Chunk::K_Conditional)));
EXPECT_THAT((const DirectiveMap::Code &)PP.Chunks[0], tokensAre(S, "a"));
const DirectiveMap::Directive &Endif(PP.Chunks[1]);
EXPECT_EQ(Endif.Kind, tok::pp_endif);
EXPECT_THAT(Endif, tokensAre(S, "# endif // mismatched"));
const DirectiveMap::Conditional &X(PP.Chunks[2]);
EXPECT_EQ(1u, X.Branches.size());
// The (only) branch of the broken conditional section runs until eof.
EXPECT_EQ(tok::pp_if, X.Branches.front().first.Kind);
EXPECT_THAT(X.Branches.front().second.Chunks,
ElementsAre(chunkKind(Chunk::K_Code)));
// The missing terminating directive is marked as pp_not_keyword.
EXPECT_EQ(tok::pp_not_keyword, X.End.Kind);
EXPECT_EQ(0u, X.End.Tokens.size());
}
} // namespace
} // namespace pseudo
} // namespace clang