0.5.4
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
translation_unit_visitor.cc
Go to the documentation of this file.
1/**
2 * @file src/include_diagram/visitor/translation_unit_visitor.cc
3 *
4 * Copyright (c) 2021-2024 Bartek Kryza <bkryza@gmail.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
20
21#include "common/clang_utils.h"
22
23#include <filesystem>
24
26
28 clang::SourceManager & /*sm*/,
31 : diagram_{diagram}
32 , config_{config}
33{
34}
35
37 clang::SourceManager &sm,
40 : visitor_specialization_t{sm, diagram, config}
41{
42}
43
44#if LLVM_VERSION_MAJOR >= 16
46 clang::SourceLocation hash_loc, const clang::Token & /*include_tok*/,
47 clang::StringRef /*file_name*/, bool is_angled,
48 clang::CharSourceRange /*filename_range*/, clang::OptionalFileEntryRef file,
49 clang::StringRef /*search_path*/, clang::StringRef relative_path,
50 const clang::Module * /*imported*/,
51 clang::SrcMgr::CharacteristicKind file_type)
52#elif LLVM_VERSION_MAJOR > 14
54 clang::SourceLocation hash_loc, const clang::Token & /*include_tok*/,
55 clang::StringRef /*file_name*/, bool is_angled,
56 clang::CharSourceRange /*filename_range*/,
57 clang::Optional<clang::FileEntryRef> file, clang::StringRef /*search_path*/,
58 clang::StringRef relative_path, const clang::Module * /*imported*/,
59 clang::SrcMgr::CharacteristicKind file_type)
60#else
62 clang::SourceLocation hash_loc, const clang::Token & /*include_tok*/,
63 clang::StringRef /*file_name*/, bool is_angled,
64 clang::CharSourceRange /*filename_range*/, const clang::FileEntry *file,
65 clang::StringRef /*search_path*/, clang::StringRef relative_path,
66 const clang::Module * /*imported*/,
67 clang::SrcMgr::CharacteristicKind file_type)
68#endif
69{
73
74 auto current_file =
75 std::filesystem::path{source_manager().getFilename(hash_loc).str()};
76 current_file = std::filesystem::absolute(current_file);
77 current_file = current_file.lexically_normal();
78
79 auto current_file_id = process_source_file(current_file);
80 if (!current_file_id)
81 return;
82
83 assert(diagram().get(current_file_id.value()));
84
85#if LLVM_VERSION_MAJOR > 14
86 if (!file.has_value())
87 return;
88 auto include_path = std::filesystem::path(file->getDir().getName().str());
89#else
90 if (file == nullptr)
91 return;
92 auto include_path = std::filesystem::path(file->getDir()->getName().str());
93#endif
94 include_path = include_path / file->getName().str();
95 include_path = include_path.lexically_normal();
96
97 LOG_DBG("Processing include directive {} in file {}", include_path.string(),
98 current_file.string());
99
100 auto relative_include_path =
101 std::filesystem::relative(include_path, config().root_directory());
102
103 if (diagram().should_include(source_file{include_path})) {
104 process_internal_header(include_path,
105 file_type != clang::SrcMgr::CharacteristicKind::C_User,
106 current_file_id.value());
107 }
108 else if (config().generate_system_headers() && is_angled) {
109 process_external_system_header(
110 relative_path.str(), current_file_id.value());
111 }
112 else {
113 LOG_DBG("Skipping include directive to file {}", include_path.string());
114 }
115}
116
118 const std::filesystem::path &include_path, bool is_system,
119 const eid_t current_file_id)
120{
121 // Make the path relative with respect to relative_to config option
122 auto relative_include_path =
123 std::filesystem::relative(include_path, config().root_directory());
124
125 // Check if this source file is already registered in the diagram,
126 // if not add it
127 auto diagram_path =
128 common::model::source_file{relative_include_path}.full_path();
129 if (!diagram().get_element(diagram_path.to_string()).has_value()) {
130 diagram().add_file(std::make_unique<common::model::source_file>(
131 diagram_path.to_string()));
132 }
133
134 auto &include_file = diagram().get_element(diagram_path).value();
135
136 include_file.set_type(common::model::source_file_t::kHeader);
137 include_file.set_file(
138 std::filesystem::absolute(include_path).lexically_normal().string());
139 include_file.set_line(0);
140 include_file.set_system_header(is_system);
141
142 // Add relationship from the currently parsed source file to this
143 // include file
144 const auto relationship_type = is_system
147
148 if (diagram().get(current_file_id)) {
149 diagram()
150 .get(current_file_id)
151 .value()
152 .add_relationship(common::model::relationship{relationship_type,
153 include_file.id(), common::model::access_t::kNone});
154 }
155}
156
158 const std::filesystem::path &include_path, const eid_t current_file_id)
159{
160 const auto file_name = include_path.filename();
161 const auto file_name_str = file_name.string();
162
163 auto f = std::make_unique<common::model::source_file>();
164 f->set_name(include_path.string());
166 f->set_id(common::to_id(include_path));
167 f->set_system_header(true);
168
169 const auto f_id = f->id();
170
171 diagram().add_file(std::move(f));
172
173 if (diagram().get(current_file_id)) {
174 diagram()
175 .get(current_file_id)
176 .value()
177 .add_relationship(common::model::relationship{
180 }
181}
182
183std::optional<eid_t>
185 const std::filesystem::path &file)
186{
190
191 auto file_path = std::filesystem::path{file};
192
193 // Make sure the file_path is absolute with respect to the
194 // filesystem, and in normal form
195 if (file_path.is_relative()) {
196 file_path = config().base_directory() / file_path;
197 }
198
199 file_path = file_path.lexically_normal();
200
201 if (diagram().should_include(source_file{file_path})) {
202 LOG_DBG("Processing source file {}", file.string());
203
204 // Relativize the path with respect to effective root directory
205 auto relative_file_path =
206 std::filesystem::relative(file_path, config().root_directory());
207
208 [[maybe_unused]] const auto relative_file_path_str =
209 relative_file_path.string();
210
211 // Check if this source file is already registered in the diagram,
212 // if not add it
213 auto diagram_path = source_file{relative_file_path}.full_path();
214 if (!diagram().get_element(diagram_path).has_value()) {
216 std::make_unique<source_file>(relative_file_path));
217 }
218
219 auto &source_file = diagram().get_element(diagram_path).value();
220
221 const std::string implementation_suffix_prefix{".c"};
222 if (file_path.has_extension() &&
224 file_path.extension().string(), implementation_suffix_prefix)) {
225 source_file.set_type(source_file_t::kImplementation);
226 }
227 else
228 source_file.set_type(source_file_t::kHeader);
229
230 source_file.set_file(std::filesystem::absolute(file.string())
231 .lexically_normal()
232 .string());
233
234 if (util::is_relative_to(file_path, config().root_directory())) {
235 source_file.set_file_relative(util::path_to_url(
236 relative(source_file.file(), config().root_directory())
237 .string()));
238 }
239 else {
240 source_file.set_file_relative("");
241 }
242
243 source_file.set_line(0);
244
245 return source_file.id();
246 }
247
248 return {};
249}
250
252{
253 return diagram_;
254}
255
258{
259 return config_;
260}
261
263
264} // namespace clanguml::include_diagram::visitor