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
34using namespace clang;
35using namespace clang::syntax;
36
37namespace {
38ArrayRef<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>(Val: N))
42 return llvm::ArrayRef(STM.getToken(I: L->getTokenKey()), 1);
43 auto *T = cast<syntax::Tree>(Val: N);
44 return llvm::ArrayRef(STM.getToken(I: T->findFirstLeaf()->getTokenKey()),
45 STM.getToken(I: T->findLastLeaf()->getTokenKey()) + 1);
46}
47} // namespace
48
49std::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(x: 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(x: config);
65 }
66 return all_configs;
67}
68
69syntax::TranslationUnit *
70SyntaxTreeTest::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>(args: std::move(*Tokens).consume());
86 Tokens = nullptr; // make sure we fail if this gets called twice.
87 TM = std::make_unique<syntax::TokenBufferTokenManager>(
88 args&: *TB, args: Ctx.getLangOpts(), args&: 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>(args&: 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(Path: FileName, ModificationTime: time_t(), Buffer: llvm::MemoryBuffer::getMemBufferCopy(InputData: ""));
127
128 if (!Diags->getClient())
129 Diags->setClient(client: new TextDiagnosticPrinter(llvm::errs(), DiagOpts));
130 Diags->setSeverityForGroup(Flavor: diag::Flavor::WarningOrError, Group: "unused-value",
131 Map: diag::Severity::Ignored, Loc: SourceLocation());
132
133 // Prepare to run a compiler.
134 std::vector<std::string> Args = {
135 "syntax-test",
136 "-fsyntax-only",
137 };
138 llvm::copy(Range: ClangConfig.getCommandLineArgs(), Out: std::back_inserter(x&: Args));
139 Args.push_back(x: FileName);
140
141 std::vector<const char *> ArgsCStr;
142 for (const std::string &arg : Args) {
143 ArgsCStr.push_back(x: arg.c_str());
144 }
145
146 CreateInvocationOptions CIOpts;
147 CIOpts.Diags = Diags;
148 CIOpts.VFS = FS;
149 Invocation = createInvocation(Args: ArgsCStr, Opts: std::move(CIOpts));
150 assert(Invocation);
151 Invocation->getFrontendOpts().DisableFree = false;
152 Invocation->getPreprocessorOpts().addRemappedFile(
153 From: FileName, To: llvm::MemoryBuffer::getMemBufferCopy(InputData: Code).release());
154 CompilerInstance Compiler(Invocation);
155 Compiler.setDiagnostics(Diags.get());
156 Compiler.setFileManager(FileMgr.get());
157 Compiler.setSourceManager(SourceMgr.get());
158
159 syntax::TranslationUnit *Root = nullptr;
160 BuildSyntaxTreeAction Recorder(Root, this->TM, this->TB, this->Arena);
161
162 // Action could not be executed but the frontend didn't identify any errors
163 // in the code ==> problem in setting up the action.
164 if (!Compiler.ExecuteAction(Recorder) &&
165 Diags->getClient()->getNumErrors() == 0) {
166 ADD_FAILURE() << "failed to run the frontend";
167 std::abort();
168 }
169 return Root;
170}
171
172syntax::Node *SyntaxTreeTest::nodeByRange(llvm::Annotations::Range R,
173 syntax::Node *Root) {
174 ArrayRef<syntax::Token> Toks = tokens(N: Root, STM: *TM);
175
176 if (Toks.front().location().isFileID() && Toks.back().location().isFileID() &&
177 syntax::Token::range(SM: *SourceMgr, First: Toks.front(), Last: Toks.back()) ==
178 syntax::FileRange(SourceMgr->getMainFileID(), R.Begin, R.End))
179 return Root;
180
181 auto *T = dyn_cast<syntax::Tree>(Val: Root);
182 if (!T)
183 return nullptr;
184 for (auto *C = T->getFirstChild(); C != nullptr; C = C->getNextSibling()) {
185 if (auto *Result = nodeByRange(R, Root: C))
186 return Result;
187 }
188 return nullptr;
189}
190

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang/unittests/Tooling/Syntax/TreeTestBase.cpp