llvm-project

Форк
0
357 строк · 9.7 Кб
1
//===--- DirectiveTreeTest.cpp --------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8

9
#include "clang-pseudo/DirectiveTree.h"
10

11
#include "clang-pseudo/Token.h"
12
#include "clang/Basic/LangOptions.h"
13
#include "clang/Basic/TokenKinds.h"
14
#include "llvm/ADT/StringExtras.h"
15
#include "llvm/ADT/StringRef.h"
16
#include "gmock/gmock.h"
17
#include "gtest/gtest.h"
18

19
namespace clang {
20
namespace pseudo {
21
namespace {
22

23
using testing::_;
24
using testing::ElementsAre;
25
using testing::Matcher;
26
using testing::Pair;
27
using testing::StrEq;
28
using Chunk = DirectiveTree::Chunk;
29

30
// Matches text of a list of tokens against a string (joined with spaces).
31
// e.g. EXPECT_THAT(Stream.tokens(), tokens("int main ( ) { }"));
32
MATCHER_P(tokens, Tokens, "") {
33
  std::vector<llvm::StringRef> Texts;
34
  for (const Token &Tok : arg)
35
    Texts.push_back(Tok.text());
36
  return Matcher<std::string>(StrEq(Tokens))
37
      .MatchAndExplain(llvm::join(Texts, " "), result_listener);
38
}
39

40
// Matches tokens covered a directive chunk (with a Tokens property) against a
41
// string, similar to tokens() above.
42
// e.g. EXPECT_THAT(SomeDirective, tokensAre(Stream, "# include < vector >"));
43
MATCHER_P2(tokensAre, TS, Tokens, "tokens are " + std::string(Tokens)) {
44
  return testing::Matches(tokens(Tokens))(TS.tokens(arg.Tokens));
45
}
46

47
MATCHER(directiveChunk, "") {
48
  return std::holds_alternative<DirectiveTree::Directive>(arg);
49
}
50
MATCHER(codeChunk, "") {
51
  return std::holds_alternative<DirectiveTree::Code>(arg);
52
}
53
MATCHER(conditionalChunk, "") {
54
  return std::holds_alternative<DirectiveTree::Conditional>(arg);
55
}
56

57
TEST(DirectiveTree, Parse) {
58
  LangOptions Opts;
59
  std::string Code = R"cpp(
60
  #include <foo.h>
61

62
  int main() {
63
  #ifdef HAS_FOO
64
  #if HAS_BAR
65
    foo(bar);
66
  #else
67
    foo(0)
68
  #endif
69
  #elif NEEDS_FOO
70
    #error missing_foo
71
  #endif
72
  }
73
  )cpp";
74

75
  TokenStream S = cook(lex(Code, Opts), Opts);
76
  DirectiveTree PP = DirectiveTree::parse(S);
77
  ASSERT_THAT(PP.Chunks, ElementsAre(directiveChunk(), codeChunk(),
78
                                     conditionalChunk(), codeChunk()));
79

80
  EXPECT_THAT(std::get<DirectiveTree::Directive>(PP.Chunks[0]),
81
              tokensAre(S, "# include < foo . h >"));
82
  EXPECT_THAT(std::get<DirectiveTree::Code>(PP.Chunks[1]),
83
              tokensAre(S, "int main ( ) {"));
84
  EXPECT_THAT(std::get<DirectiveTree::Code>(PP.Chunks[3]), tokensAre(S, "}"));
85

86
  const auto &Ifdef = std::get<DirectiveTree::Conditional>(PP.Chunks[2]);
87
  EXPECT_THAT(Ifdef.Branches,
88
              ElementsAre(Pair(tokensAre(S, "# ifdef HAS_FOO"), _),
89
                          Pair(tokensAre(S, "# elif NEEDS_FOO"), _)));
90
  EXPECT_THAT(Ifdef.End, tokensAre(S, "# endif"));
91

92
  const DirectiveTree &HasFoo(Ifdef.Branches[0].second);
93
  const DirectiveTree &NeedsFoo(Ifdef.Branches[1].second);
94

95
  EXPECT_THAT(HasFoo.Chunks, ElementsAre(conditionalChunk()));
96
  const auto &If = std::get<DirectiveTree::Conditional>(HasFoo.Chunks[0]);
97
  EXPECT_THAT(If.Branches, ElementsAre(Pair(tokensAre(S, "# if HAS_BAR"), _),
98
                                       Pair(tokensAre(S, "# else"), _)));
99
  EXPECT_THAT(If.Branches[0].second.Chunks, ElementsAre(codeChunk()));
100
  EXPECT_THAT(If.Branches[1].second.Chunks, ElementsAre(codeChunk()));
101

102
  EXPECT_THAT(NeedsFoo.Chunks, ElementsAre(directiveChunk()));
103
  const auto &Error = std::get<DirectiveTree::Directive>(NeedsFoo.Chunks[0]);
104
  EXPECT_THAT(Error, tokensAre(S, "# error missing_foo"));
105
  EXPECT_EQ(Error.Kind, tok::pp_error);
106
}
107

108
TEST(DirectiveTree, ParseUgly) {
109
  LangOptions Opts;
110
  std::string Code = R"cpp(
111
  /*A*/ # /*B*/ \
112
   /*C*/ \
113
define \
114
BAR /*D*/
115
/*E*/
116
)cpp";
117
  TokenStream S = cook(lex(Code, Opts), Opts);
118
  DirectiveTree PP = DirectiveTree::parse(S);
119

120
  ASSERT_THAT(PP.Chunks,
121
              ElementsAre(codeChunk(), directiveChunk(), codeChunk()));
122
  EXPECT_THAT(std::get<DirectiveTree::Code>(PP.Chunks[0]),
123
              tokensAre(S, "/*A*/"));
124
  const auto &Define = std::get<DirectiveTree::Directive>(PP.Chunks[1]);
125
  EXPECT_EQ(Define.Kind, tok::pp_define);
126
  EXPECT_THAT(Define, tokensAre(S, "# /*B*/ /*C*/ define BAR /*D*/"));
127
  EXPECT_THAT(std::get<DirectiveTree::Code>(PP.Chunks[2]),
128
              tokensAre(S, "/*E*/"));
129
}
130

131
TEST(DirectiveTree, ParseBroken) {
132
  LangOptions Opts;
133
  std::string Code = R"cpp(
134
  a
135
  #endif // mismatched
136
  #if X
137
  b
138
)cpp";
139
  TokenStream S = cook(lex(Code, Opts), Opts);
140
  DirectiveTree PP = DirectiveTree::parse(S);
141

142
  ASSERT_THAT(PP.Chunks,
143
              ElementsAre(codeChunk(), directiveChunk(), conditionalChunk()));
144
  EXPECT_THAT(std::get<DirectiveTree::Code>(PP.Chunks[0]), tokensAre(S, "a"));
145
  const auto &Endif = std::get<DirectiveTree::Directive>(PP.Chunks[1]);
146
  EXPECT_EQ(Endif.Kind, tok::pp_endif);
147
  EXPECT_THAT(Endif, tokensAre(S, "# endif // mismatched"));
148

149
  const auto &X = std::get<DirectiveTree::Conditional>(PP.Chunks[2]);
150
  EXPECT_EQ(1u, X.Branches.size());
151
  // The (only) branch of the broken conditional section runs until eof.
152
  EXPECT_EQ(tok::pp_if, X.Branches.front().first.Kind);
153
  EXPECT_THAT(X.Branches.front().second.Chunks, ElementsAre(codeChunk()));
154
  // The missing terminating directive is marked as pp_not_keyword.
155
  EXPECT_EQ(tok::pp_not_keyword, X.End.Kind);
156
  EXPECT_EQ(0u, X.End.Tokens.size());
157
}
158

159
TEST(DirectiveTree, ChooseBranches) {
160
  LangOptions Opts;
161
  const std::string Cases[] = {
162
      R"cpp(
163
        // Branches with no alternatives are taken
164
        #if COND // TAKEN
165
        int x;
166
        #endif
167
      )cpp",
168

169
      R"cpp(
170
        // Empty branches are better than nothing
171
        #if COND // TAKEN
172
        #endif
173
      )cpp",
174

175
      R"cpp(
176
        // Trivially false branches are not taken, even with no alternatives.
177
        #if 0
178
        int x;
179
        #endif
180
      )cpp",
181

182
      R"cpp(
183
        // Longer branches are preferred over shorter branches
184
        #if COND // TAKEN
185
        int x = 1;
186
        #else
187
        int x;
188
        #endif
189

190
        #if COND
191
        int x;
192
        #else // TAKEN
193
        int x = 1;
194
        #endif
195
      )cpp",
196

197
      R"cpp(
198
        // Trivially true branches are taken if previous branches are trivial.
199
        #if 1 // TAKEN
200
        #else
201
          int x = 1;
202
        #endif
203

204
        #if 0
205
          int x = 1;
206
        #elif 0
207
          int x = 2;
208
        #elif 1 // TAKEN
209
          int x;
210
        #endif
211

212
        #if 0
213
          int x = 1;
214
        #elif FOO // TAKEN
215
          int x = 2;
216
        #elif 1
217
          int x;
218
        #endif
219
      )cpp",
220

221
      R"cpp(
222
        // #else is a trivially true branch
223
        #if 0
224
          int x = 1;
225
        #elif 0
226
          int x = 2;
227
        #else // TAKEN
228
          int x;
229
        #endif
230
      )cpp",
231

232
      R"cpp(
233
        // Directives break ties, but nondirective text is more important.
234
        #if FOO
235
          #define A 1 2 3
236
        #else // TAKEN
237
          #define B 4 5 6
238
          #define C 7 8 9
239
        #endif
240

241
        #if FOO // TAKEN
242
          ;
243
          #define A 1 2 3
244
        #else
245
          #define B 4 5 6
246
          #define C 7 8 9
247
        #endif
248
      )cpp",
249

250
      R"cpp(
251
        // Avoid #error directives.
252
        #if FOO
253
          int x = 42;
254
          #error This branch is no good
255
        #else // TAKEN
256
        #endif
257

258
        #if FOO
259
          // All paths here lead to errors.
260
          int x = 42;
261
          #if 1 // TAKEN
262
            #if COND // TAKEN
263
              #error This branch is no good
264
            #else
265
              #error This one is no good either
266
            #endif
267
          #endif
268
        #else // TAKEN
269
        #endif
270
      )cpp",
271

272
      R"cpp(
273
        // Populate taken branches recursively.
274
        #if FOO // TAKEN
275
          int x = 42;
276
          #if BAR
277
            ;
278
          #else // TAKEN
279
            int y = 43;
280
          #endif
281
        #else
282
          int x;
283
          #if BAR // TAKEN
284
            int y;
285
          #else
286
            ;
287
          #endif
288
        #endif
289
      )cpp",
290
  };
291
  for (const auto &Code : Cases) {
292
    TokenStream S = cook(lex(Code, Opts), Opts);
293

294
    std::function<void(const DirectiveTree &)> Verify =
295
        [&](const DirectiveTree &M) {
296
          for (const auto &C : M.Chunks) {
297
            if (!std::holds_alternative<DirectiveTree::Conditional>(C))
298
              continue;
299
            const DirectiveTree::Conditional &Cond =
300
                std::get<DirectiveTree::Conditional>(C);
301
            for (unsigned I = 0; I < Cond.Branches.size(); ++I) {
302
              auto Directive = S.tokens(Cond.Branches[I].first.Tokens);
303
              EXPECT_EQ(I == Cond.Taken, Directive.back().text() == "// TAKEN")
304
                  << "At line " << Directive.front().Line << " of: " << Code;
305
              Verify(Cond.Branches[I].second);
306
            }
307
          }
308
        };
309

310
    DirectiveTree Tree = DirectiveTree::parse(S);
311
    chooseConditionalBranches(Tree, S);
312
    Verify(Tree);
313
  }
314
}
315

316
TEST(DirectiveTree, StripDirectives) {
317
  LangOptions Opts;
318
  std::string Code = R"cpp(
319
    #include <stddef.h>
320
    a a a
321
    #warning AAA
322
    b b b
323
    #if 1
324
      c c c
325
      #warning BBB
326
      #if 0
327
        d d d
328
        #warning CC
329
      #else
330
        e e e
331
      #endif
332
      f f f
333
      #if 0
334
        g g g
335
      #endif
336
      h h h
337
    #else
338
      i i i
339
    #endif
340
    j j j
341
  )cpp";
342
  TokenStream S = lex(Code, Opts);
343

344
  DirectiveTree Tree = DirectiveTree::parse(S);
345
  chooseConditionalBranches(Tree, S);
346
  EXPECT_THAT(Tree.stripDirectives(S).tokens(),
347
              tokens("a a a b b b c c c e e e f f f h h h j j j"));
348

349
  const DirectiveTree &Part =
350
      std::get<DirectiveTree::Conditional>(Tree.Chunks[4]).Branches[0].second;
351
  EXPECT_THAT(Part.stripDirectives(S).tokens(),
352
              tokens("c c c e e e f f f h h h"));
353
}
354

355
} // namespace
356
} // namespace pseudo
357
} // namespace clang
358

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.