1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#pragma once
5
6#include <algorithm>
7#include <cstdint>
8#include <functional>
9#include <numeric>
10#include <optional>
11#include <string>
12#include <vector>
13
14#include <QString>
15
16/*
17 * Represents a general declaration that has a form that can be
18 * described by a type, name and initializer triplet, or any such form
19 * that can be described by zero or more of those same parts.
20 *
21 * For example, it can be used to represent a C++ variable declaration
22 * such as:
23 *
24 * std::vector<int> foo = { 1, 2, 3 };
25 *
26 * Where `std::vector<int>` is the type, `foo` is the name and `{ 1, 2,
27 * 3 }` is the initializer.
28 *
29 * Similarly, it can be used to represent a non-type template parameter
30 * declaration, such as the `foo` parameter in:
31 *
32 * template<int foo = 10>
33 *
34 * Where `int` is the type, `foo` is the name and `10` is the
35 * initializer.
36 *
37 * An instance can be used to represent less information dense elements
38 * by setting one or more of the fields as the empty string.
39 *
40 * For example, a template type parameter such as `T` in:
41 *
42 * template<typename T = int>
43 *
44 * Can be represented by an instance that has an empty string as the
45 * type, `T` as the name and `int` as the initializer.
46 *
47 * In general, it can be used to represent any such element that has
48 * zero or more of the three components, albeit, in QDoc, it is
49 * specifically intended to be used to represent various C++
50 * declarations.
51 *
52 * All three fields are lowered stringified version of the original
53 * declaration, so that the type should be used at the end of a
54 * pipeline where the semantic property of the represented code are not
55 * required.
56 */
57struct ValuedDeclaration
58{
59 struct PrintingPolicy
60 {
61 bool include_type = true;
62 bool include_name = true;
63 bool include_initializer = true;
64 };
65
66 std::string type;
67 std::string name;
68 std::string initializer;
69
70 // KLUDGE: Workaround for
71 // https://stackoverflow.com/questions/53408962/try-to-understand-compiler-error-message-default-member-initializer-required-be
72 static PrintingPolicy default_printing_policy() { return PrintingPolicy{}; }
73
74 /*
75 * Constructs and returns a human-readable representation of this
76 * declaration.
77 *
78 * The constructed string is formatted so that as to rebuild a
79 * possible version of the C++ code that is modeled by an instance
80 * of this type.
81 *
82 * Each component participates in the human-presentable version if
83 * it they are not the empty string.
84 *
85 * The "type" and "name" component participate with their literal
86 * representation.
87 *
88 * The "iniitlalizer" components contributes an equal symbol,
89 * followed by a space followed by the literal representation of
90 * the component.
91 *
92 * The component contributes in an ordered way, with "type"
93 * contributing first, "name" contributing second and
94 * "initializer" contributing last.
95 *
96 * Each contribution is separated by a space if the component that
97 * comes before it, if any, has contributed to the human-readable
98 * representation.
99 *
100 * For example, an instance of this type that has "type" component
101 * "int", "name" component "foo" and "iniitializer" component
102 * "100", would be represented as:
103 *
104 * int foo = 100
105 *
106 * Where "int" is the "type" component contribution, "foo" is the
107 * "name" component contribution and "= 100" is the "initializer"
108 * component contribution.
109 * Each of those contribution is separated by a space, as each
110 * "preceding" component has contributed to the representation.
111 *
112 * If we provide a similar instance with, for example, the "type"
113 * and "name" components as the empty string, then the
114 * representation would be "= 100", which is the "initializer"
115 * component contribution, the only component that is not the
116 * empty string.
117 *
118 * The policy argument allows to treat certain components as if
119 * they were the empty string.
120 *
121 * For example, given an instance of this type that has "type"
122 * component "double", "name" component "bar" and "iniitializer"
123 * component "10.2", its human-readable representation would be
124 * "double bar = 10.2".
125 *
126 * If the representation of that same instance was obtained by
127 * using a policy that excludes the "name" component, then that
128 * representation would be "double = 10.2", which is equivalent
129 * to the representation of an instance that is the same as the
130 * orginal one with the "name" component as the empty string.
131 */
132 inline std::string to_std_string(PrintingPolicy policy = default_printing_policy()) const
133 {
134 std::string s{};
135
136 if (!type.empty() && policy.include_type)
137 s += (s.empty() ? "" : " ") + type;
138
139 if (!name.empty() && policy.include_name)
140 s += (s.empty() ? "" : " ") + name;
141
142 if (!initializer.empty() && policy.include_initializer)
143 s += (s.empty() ? "= " : " = ") + initializer;
144
145 return s;
146 }
147};
148
149struct RelaxedTemplateParameter;
150
151struct TemplateDeclarationStorage
152{
153 std::vector<RelaxedTemplateParameter> parameters;
154
155 inline std::string to_std_string() const;
156};
157
158/*
159 * Represents a C++ template parameter.
160 *
161 * The model used by this representation is a slighly simplified
162 * model.
163 *
164 * In the model, template parameters are one of:
165 *
166 * - A type template parameter.
167 * - A non type template parameter.
168 * - A template template parameter.
169 *
170 * Furthermore, each parameter can:
171 *
172 * - Be a parameter pack.
173 * - Carry an additional template declaration (as a template template
174 * parameter would).
175 * - Have no declared type.
176 * - Have no declared name.
177 * - Have no declared initializer.
178 *
179 * Due to this simplified model certain incorrect parameters can be
180 * represented.
181 *
182 * For example, it might be possible to represent a parameter pack
183 * that has a default initializer, a non-type template parameter that
184 * has no type or a template template parameter that carries no
185 * template declaration.
186 *
187 * The model further elides some of the semantic that might be carried
188 * by a parameter.
189 * For example, the model has no specific concept for template
190 * constraints.
191 *
192 * Template parameters can be represented as instances of the type.
193 *
194 * For example, a type template parameter `typename T` can be
195 * represented as the following instance:
196 *
197 * RelaxedTemplateParameter{
198 * RelaxedTemplateParameter::Kind::TypeTemplateParameter,
199 * false,
200 * {
201 * "",
202 * "T",
203 * ""
204 * },
205 * {}
206 * };
207 *
208 * And a non-type template parameter pack "int... Args" as:
209 *
210 * RelaxedTemplateParameter{
211 * RelaxedTemplateParameter::Kind::NonTypeTemplateParameter,
212 * true,
213 * {
214 * "int",
215 * "Args",
216 * ""
217 * },
218 * {}
219 * };
220 *
221 * Due to the relaxed constraint and the representable incorrect
222 * parameters, the type is intended to be used for data that is
223 * already validated and known to be correct, such as data that is
224 * extracted from Clang.
225 */
226struct RelaxedTemplateParameter
227{
228 enum class Kind : std::uint8_t {
229 TypeTemplateParameter,
230 NonTypeTemplateParameter,
231 TemplateTemplateParameter
232 };
233
234 Kind kind;
235 bool is_parameter_pack;
236 ValuedDeclaration valued_declaration;
237 std::optional<TemplateDeclarationStorage> template_declaration;
238
239 /*
240 * Constructs and returns a human-readable representation of this
241 * parameter.
242 *
243 * The constructed string is formatted so that as to rebuild a
244 * possible version of the C++ code that is modeled by an instance
245 * of this type.
246 *
247 * The format of the representation varies based on the "kind" of
248 * the parameter.
249 *
250 * - A "TypeTemplateParameter", is constructed as the
251 * concatenation of the literal "typename", followed by the
252 * literal "..." if the parameter is a pack, followed by the
253 * human-readable representaion of "valued_declaration".
254 *
255 * If the human-readable representation of
256 * "valued_declaration" is not the empty string, it is
257 * preceded by a space when it contributes to the
258 * representation.
259 *
260 * For example, the C++ type template parameter "typename Foo
261 * = int", would be represented by the instance:
262 *
263 * RelaxedTemplateParameter{
264 * RelaxedTemplateParameter::Kind::TypeTemplateParameter,
265 * false,
266 * {
267 * "",
268 * "Foo",
269 * "int"
270 * },
271 * {}
272 * };
273 *
274 * And its representation would be:
275 *
276 * typename Foo = int
277 *
278 * Where "typename" is the added literal and "Foo = int" is
279 * the representation for "valued_declaration", with a space
280 * in-between the two contributions.
281 *
282 * - A "NonTypeTemplateParameter", is constructed by the
283 * contribution of the "type" compoment of "valued_declaration",
284 * followed by the literal "..." if the parameter is a pack,
285 * followed by the human-presentable version of
286 * "valued_declaration" without its "type" component
287 * contribution.
288 *
289 * If the contribution of the "type" component of
290 * "valued_declaration" is not empty, the next contribution is
291 * preceded by a space.
292 *
293 * For example, the C++ non-type template parameter "int...
294 * SIZE", would be represented by the instance:
295 *
296 *
297 * RelaxedTemplateParameter{
298 * RelaxedTemplateParameter::Kind::NonTypeTemplateParameter,
299 * true,
300 * {
301 * "int",
302 * "SIZE",
303 * ""
304 * },
305 * {}
306 * };
307 *
308 * And its representation would be:
309 *
310 * int... SIZE
311 *
312 * Where "int" is the "type" component contribution of
313 * "valued_declaration", "..." is the added literal due to
314 * the parameter being a pack and " SIZE" being the
315 * human-readable representation of "valued_declaration"
316 * without its "type" component contribution, preceded by a
317 * space.
318 *
319 * - A "TemplateTemplateParameter", is constructed by the
320 * contribution of the human-presentable representation of
321 * "template_declaration", followed by the representation of
322 * this parameter if it was a "TypeTemplateParameter", with a
323 * space between the two contributions if the
324 * human-presentable representation of "template_declaration"
325 * is not empty.
326 *
327 * For example, the C++ template template template parameter
328 * "template<typename> T", would be represented by the
329 * instance:
330 *
331 *
332 * RelaxedTemplateParameter{
333 * RelaxedTemplateParameter::Kind::TemplateTemplateParameter,
334 * false,
335 * {
336 * "",
337 * "T",
338 * ""
339 * },
340 * {
341 * RelaxedTemplateParameter{
342 * RelaxedTemplateParameter::Kind::TypeTemplateParameter,
343 * false,
344 * {
345 * "",
346 * "",
347 * ""
348 * },
349 * {}
350 * }
351 * }
352 * };
353 *
354 * And its representation would be:
355 *
356 * template <typename> typename T
357 *
358 * Where "template <typename>" human-presentable version of
359 * "template_declaration" and "typename T" is the
360 * human-presentable version of this parameter if it was a
361 * type template parameter.
362 *
363 * With a space between the two contributions.
364 */
365 inline std::string to_std_string() const
366 {
367 switch (kind) {
368 // TODO: This can probably be moved under the template
369 // template parameter case and reused through a fallback.
370 case Kind::TypeTemplateParameter: {
371 std::string valued_declaration_string = valued_declaration.to_std_string();
372
373 return std::string("typename") + (is_parameter_pack ? "..." : "")
374 + (valued_declaration_string.empty() ? "" : " ") + valued_declaration_string;
375 }
376 case Kind::NonTypeTemplateParameter: {
377 std::string type_string = valued_declaration.type + (is_parameter_pack ? "..." : "");
378
379 return type_string + (type_string.empty() ? "" : " ")
380 + valued_declaration.to_std_string(
381 policy: ValuedDeclaration::PrintingPolicy{ .include_type: false, .include_name: true, .include_initializer: true });
382 }
383 case Kind::TemplateTemplateParameter: {
384 std::string valued_declaration_string = valued_declaration.to_std_string();
385
386 return (template_declaration ? (*template_declaration).to_std_string() + " " : "")
387 + "typename" + (is_parameter_pack ? "..." : "")
388 + (valued_declaration_string.empty() ? "" : " ") + valued_declaration_string;
389 }
390 default:
391 return "";
392 }
393 }
394};
395
396/*
397 * Represents a C++ template declaration as a collection of template
398 * parameters.
399 *
400 * The parameters for the declaration follow the same relaxed rules as
401 * `RelaxedTemplateParameter` and inherit the possibility of
402 * representing incorrect declarations.
403 *
404 * Due to the relaxed constraint and the representable incorrect
405 * parameters, the type is intended to be used for data that is
406 * already validated and known to be correct, such as data that is
407 * extracted from Clang.
408 */
409struct RelaxedTemplateDeclaration : TemplateDeclarationStorage
410{
411 inline QString to_qstring() const { return QString::fromStdString(s: to_std_string()); }
412};
413
414/*
415 * Constructs and returns a human-readable representation of this
416 * declaration.
417 *
418 * The constructed string is formatted so as to rebuild a
419 * possible version of the C++ code that is modeled by an instance
420 * of this type.
421 *
422 * The representation of a declaration is constructed by the literal
423 * "template <", followed by the human-presentable version of each
424 * parameter in "parameters", with a comma and a space between each
425 * parameter, followed by a closing literal ">".
426 *
427 * For example, the empty declaration is represented as "template <>".
428 *
429 * While a template declaration that has a type template parameter
430 * "Foo" with initializer "int" and a non-type template parameter pack
431 * with type "int" and name "S" would be represented as:
432 *
433 * template <typename Foo = int, int... S>
434 */
435inline std::string TemplateDeclarationStorage::to_std_string() const
436{
437 if (parameters.empty())
438 return "template <>";
439
440 return "template <"
441 + std::accumulate(first: std::next(x: parameters.cbegin()), last: parameters.cend(),
442 init: parameters.front().to_std_string(),
443 binary_op: [](auto &&acc, const RelaxedTemplateParameter &parameter) {
444 return acc + ", " + parameter.to_std_string();
445 })
446 + ">";
447}
448
449/*
450 * Returns true if the two template declaration represented by left
451 * and right are substitutable.
452 *
453 * QDoc uses a simplified model for template declarations and,
454 * similarly, uses a simplified model of "substitutability".
455 *
456 * Two declarations are substitutable if:
457 *
458 * - They have the same amount of parameters
459 * - For each pair of parameters with the same postion:
460 * - They have the same kind
461 * - They are both parameter packs or both are not parameter packs
462 * - If they are non-type template parameters then they have the same type
463 * - If they are both template template parameters then they both
464 * carry an additional template declaration and the additional
465 * template declarations are substitutable
466 *
467 * This means that in the simplified models, we generally ignore default arguments, name and such.
468 *
469 * This model does not follow the way C++ performs disambiguation but
470 * should be enough to handle most cases in the documentation.
471 */
472inline bool are_template_declarations_substitutable(const TemplateDeclarationStorage& left, const TemplateDeclarationStorage& right) {
473 static auto are_template_parameters_substitutable = [](const RelaxedTemplateParameter& left, const RelaxedTemplateParameter& right) {
474 if (left.kind != right.kind) return false;
475 if (left.is_parameter_pack != right.is_parameter_pack) return false;
476
477 if (left.kind == RelaxedTemplateParameter::Kind::NonTypeTemplateParameter &&
478 (left.valued_declaration.type != right.valued_declaration.type))
479 return false;
480
481 if (left.kind == RelaxedTemplateParameter::Kind::TemplateTemplateParameter) {
482 if (!left.template_declaration && right.template_declaration) return false;
483 if (left.template_declaration && !right.template_declaration) return false;
484
485 if (left.template_declaration && right.template_declaration)
486 return are_template_declarations_substitutable(left: *left.template_declaration, right: *right.template_declaration);
487 }
488
489 return true;
490 };
491
492 const auto& left_parameters = left.parameters;
493 const auto& right_parameters = right.parameters;
494
495 if (left_parameters.size() != right_parameters.size()) return false;
496
497 return std::transform_reduce(first1: left_parameters.cbegin(), last1: left_parameters.cend(), first2: right_parameters.cbegin(),
498 init: true,
499 binary_op1: std::logical_and<bool>{},
500 binary_op2: are_template_parameters_substitutable
501 );
502}
503

source code of qttools/src/qdoc/qdoc/src/qdoc/template_declaration.h