//===--- DirectiveTree.h - Find and strip preprocessor directives *- C++-*-===// // // 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 // //===----------------------------------------------------------------------===// // // The pseudoparser tries to match a token stream to the C++ grammar. // Preprocessor #defines and other directives are not part of this grammar, and // should be removed before the file can be parsed. // // Conditional blocks like #if...#else...#endif are particularly tricky, as // simply stripping the directives may not produce a grammatical result: // // return // #ifndef DEBUG // 1 // #else // 0 // #endif // ; // // This header supports analyzing and removing the directives in a source file. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_DIRECTIVETREE_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_DIRECTIVETREE_H #include "Token.h" #include "clang/Basic/TokenKinds.h" #include <optional> #include <variant> #include <vector> namespace clang { namespace clangd { /// Describes the structure of a source file, as seen by the preprocessor. /// /// The structure is a tree, whose leaves are plain source code and directives, /// and whose internal nodes are #if...#endif sections. /// /// (root) /// |-+ Directive #include <stdio.h> /// |-+ Code int main() { /// | ` printf("hello, "); /// |-+ Conditional -+ Directive #ifndef NDEBUG /// | |-+ Code printf("debug\n"); /// | |-+ Directive #else /// | |-+ Code printf("production\n"); /// | `-+ Directive #endif /// |-+ Code return 0; /// ` } /// /// Unlike the clang preprocessor, we model the full tree explicitly. /// This class does not recognize macro usage, only directives. struct DirectiveTree { … }; llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DirectiveTree &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DirectiveTree::Code &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DirectiveTree::Directive &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DirectiveTree::Conditional &); /// Selects a "taken" branch for each conditional directive in the file. /// /// The choice is somewhat arbitrary, but aims to produce a useful parse: /// - idioms like `#if 0` are respected /// - we avoid paths that reach `#error` /// - we try to maximize the amount of code seen /// The choice may also be "no branch taken". /// /// Choices are also made for conditionals themselves inside not-taken branches: /// #if 1 // taken! /// #else // not taken /// #if 1 // taken! /// #endif /// #endif /// /// The choices are stored in Conditional::Taken nodes. void chooseConditionalBranches(DirectiveTree &, const TokenStream &Code); } // namespace clangd } // namespace clang #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_DIRECTIVETREE_H