xref: /llvm-project/clang-tools-extra/clangd/support/DirectiveTree.cpp (revision f59b600c21add076d6a876f29f94990b24b8e321)
1ed8f7882SAaron Ballman //===--- DirectiveTree.cpp - Find and strip preprocessor directives -------===//
2ed8f7882SAaron Ballman //
3ed8f7882SAaron Ballman // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4ed8f7882SAaron Ballman // See https://llvm.org/LICENSE.txt for license information.
5ed8f7882SAaron Ballman // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ed8f7882SAaron Ballman //
7ed8f7882SAaron Ballman //===----------------------------------------------------------------------===//
8ed8f7882SAaron Ballman 
9ed8f7882SAaron Ballman #include "DirectiveTree.h"
10ed8f7882SAaron Ballman #include "clang/Basic/IdentifierTable.h"
11ed8f7882SAaron Ballman #include "clang/Basic/TokenKinds.h"
12ed8f7882SAaron Ballman #include "llvm/Support/FormatVariadic.h"
13ed8f7882SAaron Ballman #include <optional>
14ed8f7882SAaron Ballman #include <variant>
15ed8f7882SAaron Ballman 
16ed8f7882SAaron Ballman namespace clang {
17ed8f7882SAaron Ballman namespace clangd {
18ed8f7882SAaron Ballman namespace {
19ed8f7882SAaron Ballman 
20ed8f7882SAaron Ballman class DirectiveParser {
21ed8f7882SAaron Ballman public:
22ed8f7882SAaron Ballman   explicit DirectiveParser(const TokenStream &Code)
23ed8f7882SAaron Ballman       : Code(Code), Tok(&Code.front()) {}
24ed8f7882SAaron Ballman   void parse(DirectiveTree *Result) { parse(Result, /*TopLevel=*/true); }
25ed8f7882SAaron Ballman 
26ed8f7882SAaron Ballman private:
27ed8f7882SAaron Ballman   // Roles that a directive might take within a conditional block.
28ed8f7882SAaron Ballman   enum class Cond { None, If, Else, End };
29ed8f7882SAaron Ballman   static Cond classifyDirective(tok::PPKeywordKind K) {
30ed8f7882SAaron Ballman     switch (K) {
31ed8f7882SAaron Ballman     case clang::tok::pp_if:
32ed8f7882SAaron Ballman     case clang::tok::pp_ifdef:
33ed8f7882SAaron Ballman     case clang::tok::pp_ifndef:
34ed8f7882SAaron Ballman       return Cond::If;
35ed8f7882SAaron Ballman     case clang::tok::pp_elif:
36ed8f7882SAaron Ballman     case clang::tok::pp_elifdef:
37ed8f7882SAaron Ballman     case clang::tok::pp_elifndef:
38ed8f7882SAaron Ballman     case clang::tok::pp_else:
39ed8f7882SAaron Ballman       return Cond::Else;
40ed8f7882SAaron Ballman     case clang::tok::pp_endif:
41ed8f7882SAaron Ballman       return Cond::End;
42ed8f7882SAaron Ballman     default:
43ed8f7882SAaron Ballman       return Cond::None;
44ed8f7882SAaron Ballman     }
45ed8f7882SAaron Ballman   }
46ed8f7882SAaron Ballman 
47ed8f7882SAaron Ballman   // Parses tokens starting at Tok into Tree.
48ed8f7882SAaron Ballman   // If we reach an End or Else directive that ends Tree, returns it.
49ed8f7882SAaron Ballman   // If TopLevel is true, then we do not expect End and always return
50ed8f7882SAaron Ballman   // std::nullopt.
51ed8f7882SAaron Ballman   std::optional<DirectiveTree::Directive> parse(DirectiveTree *Tree,
52ed8f7882SAaron Ballman                                                 bool TopLevel) {
53ed8f7882SAaron Ballman     auto StartsDirective =
54ed8f7882SAaron Ballman         [&, AllowDirectiveAt((const Token *)nullptr)]() mutable {
55ed8f7882SAaron Ballman           if (Tok->flag(LexFlags::StartsPPLine)) {
56ed8f7882SAaron Ballman             // If we considered a comment at the start of a PP-line, it doesn't
57ed8f7882SAaron Ballman             // start a directive but the directive can still start after it.
58ed8f7882SAaron Ballman             if (Tok->Kind == tok::comment)
59ed8f7882SAaron Ballman               AllowDirectiveAt = Tok + 1;
60ed8f7882SAaron Ballman             return Tok->Kind == tok::hash;
61ed8f7882SAaron Ballman           }
62ed8f7882SAaron Ballman           return Tok->Kind == tok::hash && AllowDirectiveAt == Tok;
63ed8f7882SAaron Ballman         };
64ed8f7882SAaron Ballman     // Each iteration adds one chunk (or returns, if we see #endif).
65ed8f7882SAaron Ballman     while (Tok->Kind != tok::eof) {
66ed8f7882SAaron Ballman       // If there's no directive here, we have a code chunk.
67ed8f7882SAaron Ballman       if (!StartsDirective()) {
68ed8f7882SAaron Ballman         const Token *Start = Tok;
69ed8f7882SAaron Ballman         do
70ed8f7882SAaron Ballman           ++Tok;
71ed8f7882SAaron Ballman         while (Tok->Kind != tok::eof && !StartsDirective());
72ed8f7882SAaron Ballman         Tree->Chunks.push_back(DirectiveTree::Code{
73ed8f7882SAaron Ballman             Token::Range{Code.index(*Start), Code.index(*Tok)}});
74ed8f7882SAaron Ballman         continue;
75ed8f7882SAaron Ballman       }
76ed8f7882SAaron Ballman 
77ed8f7882SAaron Ballman       // We have some kind of directive.
78ed8f7882SAaron Ballman       DirectiveTree::Directive Directive;
79ed8f7882SAaron Ballman       parseDirective(&Directive);
80ed8f7882SAaron Ballman       Cond Kind = classifyDirective(Directive.Kind);
81ed8f7882SAaron Ballman       if (Kind == Cond::If) {
82ed8f7882SAaron Ballman         // #if or similar, starting a nested conditional block.
83ed8f7882SAaron Ballman         DirectiveTree::Conditional Conditional;
84ed8f7882SAaron Ballman         Conditional.Branches.emplace_back();
85ed8f7882SAaron Ballman         Conditional.Branches.back().first = std::move(Directive);
86ed8f7882SAaron Ballman         parseConditional(&Conditional);
87ed8f7882SAaron Ballman         Tree->Chunks.push_back(std::move(Conditional));
88ed8f7882SAaron Ballman       } else if ((Kind == Cond::Else || Kind == Cond::End) && !TopLevel) {
89ed8f7882SAaron Ballman         // #endif or similar, ending this PStructure scope.
90ed8f7882SAaron Ballman         // (#endif is unexpected at the top level, treat as simple directive).
91ed8f7882SAaron Ballman         return std::move(Directive);
92ed8f7882SAaron Ballman       } else {
93ed8f7882SAaron Ballman         // #define or similar, a simple directive at the current scope.
94ed8f7882SAaron Ballman         Tree->Chunks.push_back(std::move(Directive));
95ed8f7882SAaron Ballman       }
96ed8f7882SAaron Ballman     }
97ed8f7882SAaron Ballman     return std::nullopt;
98ed8f7882SAaron Ballman   }
99ed8f7882SAaron Ballman 
100ed8f7882SAaron Ballman   // Parse the rest of a conditional section, after seeing the If directive.
101ed8f7882SAaron Ballman   // Returns after consuming the End directive.
102ed8f7882SAaron Ballman   void parseConditional(DirectiveTree::Conditional *C) {
103ed8f7882SAaron Ballman     assert(C->Branches.size() == 1 &&
104ed8f7882SAaron Ballman            C->Branches.front().second.Chunks.empty() &&
105ed8f7882SAaron Ballman            "Should be ready to parse first branch body");
106ed8f7882SAaron Ballman     while (Tok->Kind != tok::eof) {
107ed8f7882SAaron Ballman       auto Terminator = parse(&C->Branches.back().second, /*TopLevel=*/false);
108ed8f7882SAaron Ballman       if (!Terminator) {
109ed8f7882SAaron Ballman         assert(Tok->Kind == tok::eof && "gave up parsing before eof?");
110ed8f7882SAaron Ballman         C->End.Tokens = Token::Range::emptyAt(Code.index(*Tok));
111ed8f7882SAaron Ballman         return;
112ed8f7882SAaron Ballman       }
113ed8f7882SAaron Ballman       if (classifyDirective(Terminator->Kind) == Cond::End) {
114ed8f7882SAaron Ballman         C->End = std::move(*Terminator);
115ed8f7882SAaron Ballman         return;
116ed8f7882SAaron Ballman       }
117ed8f7882SAaron Ballman       assert(classifyDirective(Terminator->Kind) == Cond::Else &&
118ed8f7882SAaron Ballman              "ended branch unexpectedly");
119ed8f7882SAaron Ballman       C->Branches.emplace_back();
120ed8f7882SAaron Ballman       C->Branches.back().first = std::move(*Terminator);
121ed8f7882SAaron Ballman     }
122ed8f7882SAaron Ballman   }
123ed8f7882SAaron Ballman 
124ed8f7882SAaron Ballman   // Parse a directive. Tok is the hash.
125ed8f7882SAaron Ballman   void parseDirective(DirectiveTree::Directive *D) {
126ed8f7882SAaron Ballman     assert(Tok->Kind == tok::hash);
127ed8f7882SAaron Ballman 
128ed8f7882SAaron Ballman     // Directive spans from the hash until the end of line or file.
129ed8f7882SAaron Ballman     const Token *Begin = Tok++;
130ed8f7882SAaron Ballman     while (Tok->Kind != tok::eof && !Tok->flag(LexFlags::StartsPPLine))
131ed8f7882SAaron Ballman       ++Tok;
132ed8f7882SAaron Ballman     ArrayRef<Token> Tokens{Begin, Tok};
133ed8f7882SAaron Ballman     D->Tokens = {Code.index(*Tokens.begin()), Code.index(*Tokens.end())};
134ed8f7882SAaron Ballman 
135ed8f7882SAaron Ballman     // Directive name is the first non-comment token after the hash.
136ed8f7882SAaron Ballman     Tokens = Tokens.drop_front().drop_while(
137ed8f7882SAaron Ballman         [](const Token &T) { return T.Kind == tok::comment; });
138ed8f7882SAaron Ballman     if (!Tokens.empty())
139ed8f7882SAaron Ballman       D->Kind = PPKeywords.get(Tokens.front().text()).getPPKeywordID();
140ed8f7882SAaron Ballman   }
141ed8f7882SAaron Ballman 
142ed8f7882SAaron Ballman   const TokenStream &Code;
143ed8f7882SAaron Ballman   const Token *Tok;
144ed8f7882SAaron Ballman   clang::IdentifierTable PPKeywords;
145ed8f7882SAaron Ballman };
146ed8f7882SAaron Ballman 
147ed8f7882SAaron Ballman struct Dumper {
148ed8f7882SAaron Ballman   llvm::raw_ostream &OS;
149ed8f7882SAaron Ballman   unsigned Indent = 0;
150ed8f7882SAaron Ballman 
151ed8f7882SAaron Ballman   Dumper(llvm::raw_ostream& OS) : OS(OS) {}
152ed8f7882SAaron Ballman   void operator()(const DirectiveTree& Tree) {
153ed8f7882SAaron Ballman     for (const auto& Chunk : Tree.Chunks)
154ed8f7882SAaron Ballman       std::visit(*this, Chunk);
155ed8f7882SAaron Ballman   }
156ed8f7882SAaron Ballman   void operator()(const DirectiveTree::Conditional &Conditional) {
157ed8f7882SAaron Ballman     for (unsigned I = 0; I < Conditional.Branches.size(); ++I) {
158ed8f7882SAaron Ballman       const auto &Branch = Conditional.Branches[I];
159ed8f7882SAaron Ballman       (*this)(Branch.first, Conditional.Taken == I);
160ed8f7882SAaron Ballman       Indent += 2;
161ed8f7882SAaron Ballman       (*this)(Branch.second);
162ed8f7882SAaron Ballman       Indent -= 2;
163ed8f7882SAaron Ballman     }
164ed8f7882SAaron Ballman     (*this)(Conditional.End);
165ed8f7882SAaron Ballman   }
166ed8f7882SAaron Ballman   void operator()(const DirectiveTree::Directive &Directive,
167ed8f7882SAaron Ballman                   bool Taken = false) {
168ed8f7882SAaron Ballman     OS.indent(Indent) << llvm::formatv(
169ed8f7882SAaron Ballman         "#{0} ({1} tokens){2}\n", tok::getPPKeywordSpelling(Directive.Kind),
170ed8f7882SAaron Ballman         Directive.Tokens.size(), Taken ? " TAKEN" : "");
171ed8f7882SAaron Ballman   }
172ed8f7882SAaron Ballman   void operator()(const DirectiveTree::Code &Code) {
173ed8f7882SAaron Ballman     OS.indent(Indent) << llvm::formatv("code ({0} tokens)\n",
174ed8f7882SAaron Ballman                                        Code.Tokens.size());
175ed8f7882SAaron Ballman   }
176ed8f7882SAaron Ballman };
177ed8f7882SAaron Ballman } // namespace
178ed8f7882SAaron Ballman 
179ed8f7882SAaron Ballman DirectiveTree DirectiveTree::parse(const TokenStream &Code) {
180ed8f7882SAaron Ballman   DirectiveTree Result;
181ed8f7882SAaron Ballman   DirectiveParser(Code).parse(&Result);
182ed8f7882SAaron Ballman   return Result;
183ed8f7882SAaron Ballman }
184ed8f7882SAaron Ballman 
185ed8f7882SAaron Ballman // Define operator<< in terms of dump() functions above.
186ed8f7882SAaron Ballman #define OSTREAM_DUMP(Type)                                                     \
187ed8f7882SAaron Ballman   llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Type &T) {        \
188ed8f7882SAaron Ballman     Dumper{OS}(T);                                                         \
189ed8f7882SAaron Ballman     return OS;                                                                 \
190ed8f7882SAaron Ballman   }
191ed8f7882SAaron Ballman OSTREAM_DUMP(DirectiveTree)
192ed8f7882SAaron Ballman OSTREAM_DUMP(DirectiveTree::Directive)
193ed8f7882SAaron Ballman OSTREAM_DUMP(DirectiveTree::Conditional)
194ed8f7882SAaron Ballman OSTREAM_DUMP(DirectiveTree::Code)
195ed8f7882SAaron Ballman #undef OSTREAM_DUMP
196ed8f7882SAaron Ballman 
197ed8f7882SAaron Ballman namespace {
198ed8f7882SAaron Ballman // Makes choices about conditional branches.
199ed8f7882SAaron Ballman //
200ed8f7882SAaron Ballman // Generally it tries to maximize the amount of useful code we see.
201ed8f7882SAaron Ballman //
202ed8f7882SAaron Ballman // Caveat: each conditional is evaluated independently. Consider this code:
203ed8f7882SAaron Ballman //   #ifdef WINDOWS
204ed8f7882SAaron Ballman //     bool isWindows = true;
205ed8f7882SAaron Ballman //   #endif
206ed8f7882SAaron Ballman //   #ifndef WINDOWS
207ed8f7882SAaron Ballman //     bool isWindows = false;
208ed8f7882SAaron Ballman //   #endif
209ed8f7882SAaron Ballman // We take both branches and define isWindows twice. We could track more state
210ed8f7882SAaron Ballman // in order to produce a consistent view, but this is complex.
211ed8f7882SAaron Ballman class BranchChooser {
212ed8f7882SAaron Ballman public:
213ed8f7882SAaron Ballman   BranchChooser(const TokenStream &Code) : Code(Code) {}
214ed8f7882SAaron Ballman 
215ed8f7882SAaron Ballman   // Describes code seen by making particular branch choices. Higher is better.
216ed8f7882SAaron Ballman   struct Score {
217ed8f7882SAaron Ballman     int Tokens = 0; // excluding comments and directives
218ed8f7882SAaron Ballman     int Directives = 0;
219ed8f7882SAaron Ballman     int Errors = 0; // #error directives
220ed8f7882SAaron Ballman 
221ed8f7882SAaron Ballman     bool operator>(const Score &Other) const {
222ed8f7882SAaron Ballman       // Seeing errors is bad, other things are good.
223ed8f7882SAaron Ballman       return std::make_tuple(-Errors, Tokens, Directives) >
224ed8f7882SAaron Ballman              std::make_tuple(-Other.Errors, Other.Tokens, Other.Directives);
225ed8f7882SAaron Ballman     }
226ed8f7882SAaron Ballman 
227ed8f7882SAaron Ballman     Score &operator+=(const Score &Other) {
228ed8f7882SAaron Ballman       Tokens += Other.Tokens;
229ed8f7882SAaron Ballman       Directives += Other.Directives;
230ed8f7882SAaron Ballman       Errors += Other.Errors;
231ed8f7882SAaron Ballman       return *this;
232ed8f7882SAaron Ballman     }
233ed8f7882SAaron Ballman   };
234ed8f7882SAaron Ballman 
235ed8f7882SAaron Ballman   Score operator()(DirectiveTree::Code &C) {
236ed8f7882SAaron Ballman     Score S;
237ed8f7882SAaron Ballman     for (const Token &T : Code.tokens(C.Tokens))
238ed8f7882SAaron Ballman       if (T.Kind != tok::comment)
239ed8f7882SAaron Ballman         ++S.Tokens;
240ed8f7882SAaron Ballman     return S;
241ed8f7882SAaron Ballman   }
242ed8f7882SAaron Ballman 
243ed8f7882SAaron Ballman   Score operator()(DirectiveTree::Directive &D) {
244ed8f7882SAaron Ballman     Score S;
245ed8f7882SAaron Ballman     S.Directives = 1;
246ed8f7882SAaron Ballman     S.Errors = D.Kind == tok::pp_error;
247ed8f7882SAaron Ballman     return S;
248ed8f7882SAaron Ballman   }
249ed8f7882SAaron Ballman 
250ed8f7882SAaron Ballman   Score operator()(DirectiveTree::Conditional &C) {
251ed8f7882SAaron Ballman     Score Best;
252ed8f7882SAaron Ballman     bool MayTakeTrivial = true;
253ed8f7882SAaron Ballman     bool TookTrivial = false;
254ed8f7882SAaron Ballman 
255ed8f7882SAaron Ballman     for (unsigned I = 0; I < C.Branches.size(); ++I) {
256ed8f7882SAaron Ballman       // Walk the branch to make its nested choices in any case.
257ed8f7882SAaron Ballman       Score BranchScore = walk(C.Branches[I].second);
258ed8f7882SAaron Ballman       // If we already took an #if 1, don't consider any other branches.
259ed8f7882SAaron Ballman       if (TookTrivial)
260ed8f7882SAaron Ballman         continue;
261ed8f7882SAaron Ballman       // Is this a trivial #if 0 or #if 1?
262ed8f7882SAaron Ballman       if (auto TriviallyTaken = isTakenWhenReached(C.Branches[I].first)) {
263ed8f7882SAaron Ballman         if (!*TriviallyTaken)
264ed8f7882SAaron Ballman           continue; // Don't consider #if 0 even if it scores well.
265ed8f7882SAaron Ballman         if (MayTakeTrivial)
266ed8f7882SAaron Ballman           TookTrivial = true;
267ed8f7882SAaron Ballman       } else {
268ed8f7882SAaron Ballman         // After a nontrivial condition, #elif 1 isn't guaranteed taken.
269ed8f7882SAaron Ballman         MayTakeTrivial = false;
270ed8f7882SAaron Ballman       }
271ed8f7882SAaron Ballman       // Is this the best branch so far? (Including if it's #if 1).
272ed8f7882SAaron Ballman       if (TookTrivial || !C.Taken || BranchScore > Best) {
273ed8f7882SAaron Ballman         Best = BranchScore;
274ed8f7882SAaron Ballman         C.Taken = I;
275ed8f7882SAaron Ballman       }
276ed8f7882SAaron Ballman     }
277ed8f7882SAaron Ballman     return Best;
278ed8f7882SAaron Ballman   }
279ed8f7882SAaron Ballman   Score walk(DirectiveTree &M) {
280ed8f7882SAaron Ballman     Score S;
281ed8f7882SAaron Ballman     for (auto &C : M.Chunks)
282ed8f7882SAaron Ballman       S += std::visit(*this, C);
283ed8f7882SAaron Ballman     return S;
284ed8f7882SAaron Ballman   }
285ed8f7882SAaron Ballman 
286ed8f7882SAaron Ballman private:
287ed8f7882SAaron Ballman   // Return true if the directive starts an always-taken conditional branch,
288ed8f7882SAaron Ballman   // false if the branch is never taken, and std::nullopt otherwise.
289ed8f7882SAaron Ballman   std::optional<bool> isTakenWhenReached(const DirectiveTree::Directive &Dir) {
290ed8f7882SAaron Ballman     switch (Dir.Kind) {
291ed8f7882SAaron Ballman     case clang::tok::pp_if:
292ed8f7882SAaron Ballman     case clang::tok::pp_elif:
293ed8f7882SAaron Ballman       break; // handled below
294ed8f7882SAaron Ballman     case clang::tok::pp_else:
295ed8f7882SAaron Ballman       return true;
296ed8f7882SAaron Ballman     default: // #ifdef etc
297ed8f7882SAaron Ballman       return std::nullopt;
298ed8f7882SAaron Ballman     }
299ed8f7882SAaron Ballman 
300ed8f7882SAaron Ballman     const auto &Tokens = Code.tokens(Dir.Tokens);
301ed8f7882SAaron Ballman     assert(!Tokens.empty() && Tokens.front().Kind == tok::hash);
302ed8f7882SAaron Ballman     const Token &Name = Tokens.front().nextNC();
303ed8f7882SAaron Ballman     const Token &Value = Name.nextNC();
304ed8f7882SAaron Ballman     // Does the condition consist of exactly one token?
305ed8f7882SAaron Ballman     if (&Value >= Tokens.end() || &Value.nextNC() < Tokens.end())
306ed8f7882SAaron Ballman       return std::nullopt;
307ed8f7882SAaron Ballman     return llvm::StringSwitch<std::optional<bool>>(Value.text())
308ed8f7882SAaron Ballman         .Cases("true", "1", true)
309ed8f7882SAaron Ballman         .Cases("false", "0", false)
310ed8f7882SAaron Ballman         .Default(std::nullopt);
311ed8f7882SAaron Ballman   }
312ed8f7882SAaron Ballman 
313ed8f7882SAaron Ballman   const TokenStream &Code;
314ed8f7882SAaron Ballman };
315ed8f7882SAaron Ballman 
316ed8f7882SAaron Ballman } // namespace
317ed8f7882SAaron Ballman 
318ed8f7882SAaron Ballman void chooseConditionalBranches(DirectiveTree &Tree, const TokenStream &Code) {
319ed8f7882SAaron Ballman   BranchChooser{Code}.walk(Tree);
320ed8f7882SAaron Ballman }
321ed8f7882SAaron Ballman 
322ed8f7882SAaron Ballman namespace {
323ed8f7882SAaron Ballman class Preprocessor {
324ed8f7882SAaron Ballman   const TokenStream &In;
325ed8f7882SAaron Ballman   TokenStream &Out;
326ed8f7882SAaron Ballman 
327ed8f7882SAaron Ballman public:
328ed8f7882SAaron Ballman   Preprocessor(const TokenStream &In, TokenStream &Out) : In(In), Out(Out) {}
329ed8f7882SAaron Ballman   ~Preprocessor() { Out.finalize(); }
330ed8f7882SAaron Ballman 
331*f59b600cSZahira Ammarguellat   Preprocessor(const Preprocessor &other) = delete;
332*f59b600cSZahira Ammarguellat   Preprocessor &operator=(const Preprocessor &other) = delete;
333*f59b600cSZahira Ammarguellat 
334ed8f7882SAaron Ballman   void walk(const DirectiveTree &T) {
335ed8f7882SAaron Ballman     for (const auto &C : T.Chunks)
336ed8f7882SAaron Ballman       std::visit(*this, C);
337ed8f7882SAaron Ballman   }
338ed8f7882SAaron Ballman 
339ed8f7882SAaron Ballman   void operator()(const DirectiveTree::Code &C) {
340ed8f7882SAaron Ballman     for (const auto &Tok : In.tokens(C.Tokens))
341ed8f7882SAaron Ballman       Out.push(Tok);
342ed8f7882SAaron Ballman   }
343ed8f7882SAaron Ballman 
344ed8f7882SAaron Ballman   void operator()(const DirectiveTree::Directive &) {}
345ed8f7882SAaron Ballman 
346ed8f7882SAaron Ballman   void operator()(const DirectiveTree::Conditional &C) {
347ed8f7882SAaron Ballman     if (C.Taken)
348ed8f7882SAaron Ballman       walk(C.Branches[*C.Taken].second);
349ed8f7882SAaron Ballman   }
350ed8f7882SAaron Ballman };
351ed8f7882SAaron Ballman } // namespace
352ed8f7882SAaron Ballman 
353ed8f7882SAaron Ballman TokenStream DirectiveTree::stripDirectives(const TokenStream &In) const {
354ed8f7882SAaron Ballman   TokenStream Out;
355ed8f7882SAaron Ballman   Preprocessor(In, Out).walk(*this);
356ed8f7882SAaron Ballman   return Out;
357ed8f7882SAaron Ballman }
358ed8f7882SAaron Ballman 
359ed8f7882SAaron Ballman } // namespace clangd
360ed8f7882SAaron Ballman } // namespace clang
361