1+ // Copyright (c) 2024 Roberto Raggi <[email protected] >2+ //
3+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4+ // of this software and associated documentation files (the "Software"), to deal
5+ // in the Software without restriction, including without limitation the rights
6+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+ // copies of the Software, and to permit persons to whom the Software is
8+ // furnished to do so, subject to the following conditions:
9+ //
10+ // The above copyright notice and this permission notice shall be included in
11+ // all copies or substantial portions of the Software.
12+ //
13+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+ // SOFTWARE.
20+
21+ #include " cxx_document.h"
22+
23+ #include < cxx/ast.h>
24+ #include < cxx/ast_visitor.h>
25+ #include < cxx/control.h>
26+ #include < cxx/gcc_linux_toolchain.h>
27+ #include < cxx/lexer.h>
28+ #include < cxx/lsp/enums.h>
29+ #include < cxx/lsp/requests.h>
30+ #include < cxx/lsp/types.h>
31+ #include < cxx/macos_toolchain.h>
32+ #include < cxx/preprocessor.h>
33+ #include < cxx/private/path.h>
34+ #include < cxx/scope.h>
35+ #include < cxx/symbol_printer.h>
36+ #include < cxx/symbols.h>
37+ #include < cxx/translation_unit.h>
38+ #include < cxx/wasm32_wasi_toolchain.h>
39+ #include < cxx/windows_toolchain.h>
40+
41+ namespace cxx ::lsp {
42+
43+ namespace {
44+
45+ struct Diagnostics final : cxx::DiagnosticsClient {
46+ json messages = json::array();
47+ Vector<lsp::Diagnostic> diagnostics{messages};
48+
49+ void report (const cxx::Diagnostic& diag) override {
50+ std::string_view fileName;
51+ std::uint32_t line = 0 ;
52+ std::uint32_t column = 0 ;
53+
54+ preprocessor ()->getTokenStartPosition (diag.token (), &line, &column,
55+ &fileName);
56+
57+ std::uint32_t endLine = 0 ;
58+ std::uint32_t endColumn = 0 ;
59+
60+ preprocessor ()->getTokenEndPosition (diag.token (), &endLine, &endColumn,
61+ nullptr );
62+
63+ auto tmp = json::object ();
64+
65+ auto d = diagnostics.emplace_back ();
66+
67+ int s = std::max (int (line) - 1 , 0 );
68+ int sc = std::max (int (column) - 1 , 0 );
69+ int e = std::max (int (endLine) - 1 , 0 );
70+ int ec = std::max (int (endColumn) - 1 , 0 );
71+
72+ d.message (diag.message ());
73+ d.range ().start (lsp::Position (tmp).line (s).character (sc));
74+ d.range ().end (lsp::Position (tmp).line (e).character (ec));
75+ }
76+ };
77+
78+ } // namespace
79+
80+ struct CxxDocument ::Private {
81+ const CLI& cli;
82+ long version;
83+ Control control;
84+ Diagnostics diagnosticsClient;
85+ TranslationUnit unit{&control, &diagnosticsClient};
86+ std::shared_ptr<Toolchain> toolchain;
87+
88+ Private (const CLI& cli, long version) : cli(cli), version(version) {}
89+
90+ void configure ();
91+ };
92+
93+ void CxxDocument::Private::configure () {
94+ auto preprocesor = unit.preprocessor ();
95+
96+ auto toolchainId = cli.getSingle (" -toolchain" );
97+
98+ if (!toolchainId) {
99+ toolchainId = " wasm32" ;
100+ }
101+
102+ if (toolchainId == " darwin" || toolchainId == " macos" ) {
103+ toolchain = std::make_unique<MacOSToolchain>(preprocesor);
104+ } else if (toolchainId == " wasm32" ) {
105+ auto wasmToolchain = std::make_unique<Wasm32WasiToolchain>(preprocesor);
106+
107+ fs::path app_dir;
108+
109+ #if __wasi__
110+ app_dir = fs::path (" /usr/bin/" );
111+ #elif !defined(CXX_NO_FILESYSTEM)
112+ app_dir = std::filesystem::canonical (
113+ std::filesystem::path (cli.app_name ).remove_filename ());
114+ #elif __unix__ || __APPLE__
115+ char * app_name = realpath (cli.app_name .c_str (), nullptr );
116+ app_dir = fs::path (app_name).remove_filename ().string ();
117+ std::free (app_name);
118+ #endif
119+
120+ wasmToolchain->setAppdir (app_dir.string ());
121+
122+ if (auto paths = cli.get (" --sysroot" ); !paths.empty ()) {
123+ wasmToolchain->setSysroot (paths.back ());
124+ } else {
125+ auto sysroot_dir = app_dir / std::string (" ../lib/wasi-sysroot" );
126+ wasmToolchain->setSysroot (sysroot_dir.string ());
127+ }
128+
129+ toolchain = std::move (wasmToolchain);
130+ } else if (toolchainId == " linux" ) {
131+ std::string host;
132+ #ifdef __aarch64__
133+ host = " aarch64" ;
134+ #elif __x86_64__
135+ host = " x86_64" ;
136+ #endif
137+
138+ std::string arch = cli.getSingle (" -arch" ).value_or (host);
139+ toolchain = std::make_unique<GCCLinuxToolchain>(preprocesor, arch);
140+ } else if (toolchainId == " windows" ) {
141+ auto windowsToolchain = std::make_unique<WindowsToolchain>(preprocesor);
142+
143+ if (auto paths = cli.get (" -vctoolsdir" ); !paths.empty ()) {
144+ windowsToolchain->setVctoolsdir (paths.back ());
145+ }
146+
147+ if (auto paths = cli.get (" -winsdkdir" ); !paths.empty ()) {
148+ windowsToolchain->setWinsdkdir (paths.back ());
149+ }
150+
151+ if (auto versions = cli.get (" -winsdkversion" ); !versions.empty ()) {
152+ windowsToolchain->setWinsdkversion (versions.back ());
153+ }
154+
155+ toolchain = std::move (windowsToolchain);
156+ }
157+
158+ if (toolchain) {
159+ control.setMemoryLayout (toolchain->memoryLayout ());
160+
161+ if (!cli.opt_nostdinc ) toolchain->addSystemIncludePaths ();
162+
163+ if (!cli.opt_nostdincpp ) toolchain->addSystemCppIncludePaths ();
164+
165+ toolchain->addPredefinedMacros ();
166+ }
167+
168+ for (const auto & path : cli.get (" -I" )) {
169+ preprocesor->addSystemIncludePath (path);
170+ }
171+
172+ for (const auto & macro : cli.get (" -D" )) {
173+ auto sep = macro.find_first_of (" =" );
174+
175+ if (sep == std::string::npos) {
176+ preprocesor->defineMacro (macro, " 1" );
177+ } else {
178+ preprocesor->defineMacro (macro.substr (0 , sep), macro.substr (sep + 1 ));
179+ }
180+ }
181+
182+ for (const auto & macro : cli.get (" -U" )) {
183+ preprocesor->undefMacro (macro);
184+ }
185+ }
186+
187+ CxxDocument::CxxDocument (const CLI& cli, long version)
188+ : d(std::make_unique<Private>(cli, version)) {}
189+
190+ void CxxDocument::parse (std::string source, std::string fileName) {
191+ d->configure ();
192+
193+ auto & unit = d->unit ;
194+ auto & cli = d->cli ;
195+
196+ unit.setSource (std::move (source), fileName);
197+
198+ auto preprocessor = unit.preprocessor ();
199+ preprocessor->squeeze ();
200+
201+ unit.parse (ParserConfiguration{
202+ .checkTypes = cli.opt_fcheck ,
203+ .fuzzyTemplateResolution = true ,
204+ .staticAssert = cli.opt_fstatic_assert || cli.opt_fcheck ,
205+ .reflect = !cli.opt_fno_reflect ,
206+ .templates = cli.opt_ftemplates ,
207+ });
208+ }
209+
210+ CxxDocument::~CxxDocument () {}
211+
212+ auto CxxDocument::version () const -> long { return d->version ; }
213+
214+ auto CxxDocument::diagnostics () const -> Vector<Diagnostic> {
215+ return Vector<Diagnostic>(d->diagnosticsClient .messages );
216+ }
217+
218+ } // namespace cxx::lsp
0 commit comments