1 //===- TreeTestBase.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 // This file provides the test infrastructure for syntax trees. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "TreeTestBase.h" 14 #include "clang/AST/ASTConsumer.h" 15 #include "clang/Basic/LLVM.h" 16 #include "clang/Frontend/CompilerInstance.h" 17 #include "clang/Frontend/CompilerInvocation.h" 18 #include "clang/Frontend/FrontendAction.h" 19 #include "clang/Frontend/TextDiagnosticPrinter.h" 20 #include "clang/Lex/PreprocessorOptions.h" 21 #include "clang/Testing/CommandLineArgs.h" 22 #include "clang/Testing/TestClangConfig.h" 23 #include "clang/Tooling/Syntax/BuildTree.h" 24 #include "clang/Tooling/Syntax/Nodes.h" 25 #include "clang/Tooling/Syntax/Tokens.h" 26 #include "clang/Tooling/Syntax/Tree.h" 27 #include "llvm/ADT/ArrayRef.h" 28 #include "llvm/ADT/StringRef.h" 29 #include "llvm/Support/Casting.h" 30 #include "llvm/Support/Error.h" 31 #include "llvm/Testing/Annotations/Annotations.h" 32 #include "gtest/gtest.h" 33 34 using namespace clang; 35 using namespace clang::syntax; 36 37 namespace { 38 ArrayRef<syntax::Token> tokens(syntax::Node *N, 39 const TokenBufferTokenManager &STM) { 40 assert(N->isOriginal() && "tokens of modified nodes are not well-defined"); 41 if (auto *L = dyn_cast<syntax::Leaf>(N)) 42 return llvm::ArrayRef(STM.getToken(L->getTokenKey()), 1); 43 auto *T = cast<syntax::Tree>(N); 44 return llvm::ArrayRef(STM.getToken(T->findFirstLeaf()->getTokenKey()), 45 STM.getToken(T->findLastLeaf()->getTokenKey()) + 1); 46 } 47 } // namespace 48 49 std::vector<TestClangConfig> clang::syntax::allTestClangConfigs() { 50 std::vector<TestClangConfig> all_configs; 51 for (TestLanguage lang : { 52 #define TESTLANGUAGE(lang, version, std_flag, version_index) \ 53 Lang_##lang##version, 54 #include "clang/Testing/TestLanguage.def" 55 }) { 56 TestClangConfig config; 57 config.Language = lang; 58 config.Target = "x86_64-pc-linux-gnu"; 59 all_configs.push_back(config); 60 61 // Windows target is interesting to test because it enables 62 // `-fdelayed-template-parsing`. 63 config.Target = "x86_64-pc-win32-msvc"; 64 all_configs.push_back(config); 65 } 66 return all_configs; 67 } 68 69 syntax::TranslationUnit * 70 SyntaxTreeTest::buildTree(StringRef Code, const TestClangConfig &ClangConfig) { 71 // FIXME: this code is almost the identical to the one in TokensTest. Share 72 // it. 73 class BuildSyntaxTree : public ASTConsumer { 74 public: 75 BuildSyntaxTree(syntax::TranslationUnit *&Root, 76 std::unique_ptr<syntax::TokenBuffer> &TB, 77 std::unique_ptr<syntax::TokenBufferTokenManager> &TM, 78 std::unique_ptr<syntax::Arena> &Arena, 79 std::unique_ptr<syntax::TokenCollector> Tokens) 80 : Root(Root), TB(TB), TM(TM), Arena(Arena), Tokens(std::move(Tokens)) { 81 assert(this->Tokens); 82 } 83 84 void HandleTranslationUnit(ASTContext &Ctx) override { 85 TB = std::make_unique<syntax::TokenBuffer>(std::move(*Tokens).consume()); 86 Tokens = nullptr; // make sure we fail if this gets called twice. 87 TM = std::make_unique<syntax::TokenBufferTokenManager>( 88 *TB, Ctx.getLangOpts(), Ctx.getSourceManager()); 89 Arena = std::make_unique<syntax::Arena>(); 90 Root = syntax::buildSyntaxTree(*Arena, *TM, Ctx); 91 } 92 93 private: 94 syntax::TranslationUnit *&Root; 95 std::unique_ptr<syntax::TokenBuffer> &TB; 96 std::unique_ptr<syntax::TokenBufferTokenManager> &TM; 97 std::unique_ptr<syntax::Arena> &Arena; 98 std::unique_ptr<syntax::TokenCollector> Tokens; 99 }; 100 101 class BuildSyntaxTreeAction : public ASTFrontendAction { 102 public: 103 BuildSyntaxTreeAction(syntax::TranslationUnit *&Root, 104 std::unique_ptr<syntax::TokenBufferTokenManager> &TM, 105 std::unique_ptr<syntax::TokenBuffer> &TB, 106 std::unique_ptr<syntax::Arena> &Arena) 107 : Root(Root), TM(TM), TB(TB), Arena(Arena) {} 108 109 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, 110 StringRef InFile) override { 111 // We start recording the tokens, ast consumer will take on the result. 112 auto Tokens = 113 std::make_unique<syntax::TokenCollector>(CI.getPreprocessor()); 114 return std::make_unique<BuildSyntaxTree>(Root, TB, TM, Arena, 115 std::move(Tokens)); 116 } 117 118 private: 119 syntax::TranslationUnit *&Root; 120 std::unique_ptr<syntax::TokenBufferTokenManager> &TM; 121 std::unique_ptr<syntax::TokenBuffer> &TB; 122 std::unique_ptr<syntax::Arena> &Arena; 123 }; 124 125 constexpr const char *FileName = "./input.cpp"; 126 FS->addFile(FileName, time_t(), llvm::MemoryBuffer::getMemBufferCopy("")); 127 128 if (!Diags->getClient()) 129 Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), DiagOpts.get())); 130 Diags->setSeverityForGroup(diag::Flavor::WarningOrError, "unused-value", 131 diag::Severity::Ignored, SourceLocation()); 132 133 // Prepare to run a compiler. 134 std::vector<std::string> Args = { 135 "syntax-test", 136 "-fsyntax-only", 137 }; 138 llvm::copy(ClangConfig.getCommandLineArgs(), std::back_inserter(Args)); 139 Args.push_back(FileName); 140 141 std::vector<const char *> ArgsCStr; 142 for (const std::string &arg : Args) { 143 ArgsCStr.push_back(arg.c_str()); 144 } 145 146 CreateInvocationOptions CIOpts; 147 CIOpts.Diags = Diags; 148 CIOpts.VFS = FS; 149 Invocation = createInvocation(ArgsCStr, std::move(CIOpts)); 150 assert(Invocation); 151 Invocation->getFrontendOpts().DisableFree = false; 152 Invocation->getPreprocessorOpts().addRemappedFile( 153 FileName, llvm::MemoryBuffer::getMemBufferCopy(Code).release()); 154 CompilerInstance Compiler; 155 Compiler.setInvocation(Invocation); 156 Compiler.setDiagnostics(Diags.get()); 157 Compiler.setFileManager(FileMgr.get()); 158 Compiler.setSourceManager(SourceMgr.get()); 159 160 syntax::TranslationUnit *Root = nullptr; 161 BuildSyntaxTreeAction Recorder(Root, this->TM, this->TB, this->Arena); 162 163 // Action could not be executed but the frontend didn't identify any errors 164 // in the code ==> problem in setting up the action. 165 if (!Compiler.ExecuteAction(Recorder) && 166 Diags->getClient()->getNumErrors() == 0) { 167 ADD_FAILURE() << "failed to run the frontend"; 168 std::abort(); 169 } 170 return Root; 171 } 172 173 syntax::Node *SyntaxTreeTest::nodeByRange(llvm::Annotations::Range R, 174 syntax::Node *Root) { 175 ArrayRef<syntax::Token> Toks = tokens(Root, *TM); 176 177 if (Toks.front().location().isFileID() && Toks.back().location().isFileID() && 178 syntax::Token::range(*SourceMgr, Toks.front(), Toks.back()) == 179 syntax::FileRange(SourceMgr->getMainFileID(), R.Begin, R.End)) 180 return Root; 181 182 auto *T = dyn_cast<syntax::Tree>(Root); 183 if (!T) 184 return nullptr; 185 for (auto *C = T->getFirstChild(); C != nullptr; C = C->getNextSibling()) { 186 if (auto *Result = nodeByRange(R, C)) 187 return Result; 188 } 189 return nullptr; 190 } 191