Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
parameters.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "parameters.h"
5
6#include "codechunk.h"
7#include "tokenizer.h"
8
9#include <algorithm>
10
12
13QRegularExpression Parameters::s_varComment(R"(^/\*\s*([a-zA-Z_0-9]+)\s*\*/$)");
14
15/*!
16 \class Parameters
17
18 \brief A class for parsing and managing a function parameter list
19
20 The constructor is passed a string that is the text inside the
21 parentheses of a function declaration. The constructor parses
22 the parameter list into a vector of class Parameter.
23
24 The Parameters object is then used in function searches to find
25 the correct function node given the function name and the signature
26 of its parameters.
27 */
28
29Parameters::Parameters() : m_valid(true), m_privateSignal(false), m_tok(0), m_tokenizer(nullptr)
30{
31 // nothing.
32}
33
34Parameters::Parameters(const QString &signature)
35 : m_valid(true), m_privateSignal(false), m_tok(0), m_tokenizer(nullptr)
36{
37 if (!signature.isEmpty()) {
38 if (!parse(signature)) {
39 m_parameters.clear();
40 m_valid = false;
41 }
42 }
43}
44
45/*!
46 Get the next token from the string being parsed and store
47 it in the token variable.
48 */
49void Parameters::readToken()
50{
51 m_tok = m_tokenizer->getToken();
52}
53
54/*!
55 Return the current lexeme from the string being parsed.
56 */
57QString Parameters::lexeme()
58{
59 return m_tokenizer->lexeme();
60}
61
62/*!
63 Return the previous lexeme read from the string being parsed.
64 */
65QString Parameters::previousLexeme()
66{
67 return m_tokenizer->previousLexeme();
68}
69
70/*!
71 If the current token is \a target, read the next token and
72 return \c true. Otherwise, return false without reading the
73 next token.
74 */
75bool Parameters::match(int target)
76{
77 if (m_tok == target) {
78 readToken();
79 return true;
80 }
81 return false;
82}
83
84/*!
85 Reads input inside angled brackets, appending tokens to the code chunk held
86 by \a type, taking nested brackets, parentheses and square brackets into
87 account. Square brackets and regular parentheses are not handled separately.
88
89 Processing begins if the first token is a left angle bracket and continues
90 until a corresponding right angle bracket is found, the end of input occurs,
91 or an excess of right parentheses or square brackets are found.
92
93 After processing, the current token is the first token after the closing
94 right angle bracket, the end of file token, or the mismatched right bracket
95 or parenthesis.
96 */
97void Parameters::matchTemplateAngles(CodeChunk &type)
98{
99 if (m_tok == Tok_LeftAngle) {
100 int leftAngleDepth = 0;
101 int parenAndBraceDepth = 0;
102 do {
103 if (m_tok == Tok_LeftAngle) {
104 leftAngleDepth++;
105 } else if (m_tok == Tok_RightAngle) {
106 leftAngleDepth--;
107 } else if (m_tok == Tok_LeftParen || m_tok == Tok_LeftBrace) {
108 ++parenAndBraceDepth;
109 } else if (m_tok == Tok_RightParen || m_tok == Tok_RightBrace) {
110 if (--parenAndBraceDepth < 0)
111 return;
112 }
113 type.append(lexeme());
114 readToken();
115 } while (leftAngleDepth > 0 && m_tok != Tok_Eoi);
116 }
117}
118
119/*!
120 Uses the current tokenizer to parse the \a name and \a type
121 of the parameter.
122 */
123bool Parameters::matchTypeAndName(CodeChunk &type, QString &name)
124{
125 /*
126 This code is really hard to follow... sorry. The loop is there to match
127 Alpha::Beta::Gamma::...::Omega.
128 */
129 for (;;) {
130 bool virgin = true;
131
132 if (m_tok != Tok_Ident) {
133 /*
134 There is special processing for 'Foo::operator int()'
135 and such elsewhere. This is the only case where we
136 return something with a trailing gulbrandsen ('Foo::').
137 */
138 if (m_tok == Tok_operator)
139 return true;
140
141 /*
142 People may write 'const unsigned short' or
143 'short unsigned const' or any other permutation.
144 */
145 while (match(Tok_const) || match(Tok_volatile))
146 type.append(previousLexeme());
147 QString pending;
148 while (m_tok == Tok_signed || m_tok == Tok_int || m_tok == Tok_unsigned
149 || m_tok == Tok_short || m_tok == Tok_long || m_tok == Tok_int64) {
150 if (m_tok == Tok_signed)
151 pending = lexeme();
152 else {
153 if (m_tok == Tok_unsigned && !pending.isEmpty())
154 type.append(pending);
155 pending.clear();
156 type.append(lexeme());
157 }
158 readToken();
159 virgin = false;
160 }
161 if (!pending.isEmpty()) {
162 type.append(pending);
163 pending.clear();
164 }
165 while (match(Tok_const) || match(Tok_volatile))
166 type.append(previousLexeme());
167
168 if (match(Tok_Tilde))
169 type.append(previousLexeme());
170 }
171
172 if (virgin) {
173 if (match(Tok_Ident)) {
174 /*
175 This is a hack until we replace this "parser"
176 with the real one used in Qt Creator.
177 Is it still needed? mws 11/12/2018
178 */
179 if (lexeme() == "("
180 && ((previousLexeme() == "QT_PREPEND_NAMESPACE")
181 || (previousLexeme() == "NS"))) {
182 readToken();
183 readToken();
184 type.append(previousLexeme());
185 readToken();
186 } else
187 type.append(previousLexeme());
188 } else if (match(Tok_void) || match(Tok_int) || match(Tok_char) || match(Tok_double)
189 || match(Tok_Ellipsis)) {
190 type.append(previousLexeme());
191 } else {
192 return false;
193 }
194 } else if (match(Tok_int) || match(Tok_char) || match(Tok_double)) {
195 type.append(previousLexeme());
196 }
197
198 matchTemplateAngles(type);
199
200 while (match(Tok_const) || match(Tok_volatile))
201 type.append(previousLexeme());
202
203 if (match(Tok_Gulbrandsen))
204 type.append(previousLexeme());
205 else
206 break;
207 }
208
209 while (match(Tok_Ampersand) || match(Tok_Aster) || match(Tok_const) || match(Tok_Caret)
210 || match(Tok_Ellipsis))
211 type.append(previousLexeme());
212
213 if (match(Tok_LeftParenAster)) {
214 /*
215 A function pointer. This would be rather hard to handle without a
216 tokenizer hack, because a type can be followed with a left parenthesis
217 in some cases (e.g., 'operator int()'). The tokenizer recognizes '(*'
218 as a single token.
219 */
220 type.append(" "); // force a space after the type
221 type.append(previousLexeme());
222 type.appendHotspot();
223 if (match(Tok_Ident))
224 name = previousLexeme();
225 if (!match(Tok_RightParen))
226 return false;
227 type.append(previousLexeme());
228 if (!match(Tok_LeftParen))
229 return false;
230 type.append(previousLexeme());
231
232 /* parse the parameters. Ignore the parameter name from the type */
233 while (m_tok != Tok_RightParen && m_tok != Tok_Eoi) {
234 QString dummy;
235 if (!matchTypeAndName(type, dummy))
236 return false;
237 if (match(Tok_Comma))
238 type.append(previousLexeme());
239 }
240 if (!match(Tok_RightParen))
241 return false;
242 type.append(previousLexeme());
243 } else {
244 /*
245 The common case: Look for an optional identifier, then for
246 some array brackets.
247 */
248 type.appendHotspot();
249
250 if (match(Tok_Ident)) {
251 name = previousLexeme();
252 } else if (match(Tok_Comment)) {
253 // Use a regular expression to extract any commented out parameter name.
254 auto match = s_varComment.match(previousLexeme());
255 if (match.hasMatch())
256 name = match.captured(1);
257 } else if (match(Tok_LeftParen)) {
258 name = "(";
259 while (m_tok != Tok_RightParen && m_tok != Tok_Eoi) {
260 name.append(lexeme());
261 readToken();
262 }
263 name.append(")");
264 readToken();
265 if (match(Tok_LeftBracket)) {
266 name.append("[");
267 while (m_tok != Tok_RightBracket && m_tok != Tok_Eoi) {
268 name.append(lexeme());
269 readToken();
270 }
271 name.append("]");
272 readToken();
273 }
274 }
275
276 if (m_tok == Tok_LeftBracket) {
277 int bracketDepth0 = m_tokenizer->bracketDepth();
278 while ((m_tokenizer->bracketDepth() >= bracketDepth0 && m_tok != Tok_Eoi)
279 || m_tok == Tok_RightBracket) {
280 type.append(lexeme());
281 readToken();
282 }
283 }
284 }
285 return true;
286}
287
288/*!
289 Parse the next function parameter, if there is one, and
290 append it to the internal parameter vector. Return true
291 if a parameter is parsed correctly. Otherwise return false.
292 */
293bool Parameters::matchParameter()
294{
295 if (match(Tok_QPrivateSignal)) {
296 m_privateSignal = true;
297 return true;
298 }
299
300 CodeChunk chunk;
301 QString name;
302 if (!matchTypeAndName(chunk, name))
303 return false;
304 QString type = chunk.toString();
305 QString defaultValue;
306 match(Tok_Comment);
307 if (match(Tok_Equal)) {
308 chunk.clear();
309 int pdepth = m_tokenizer->parenDepth();
310 while (m_tokenizer->parenDepth() >= pdepth
311 && (m_tok != Tok_Comma || (m_tokenizer->parenDepth() > pdepth))
312 && m_tok != Tok_Eoi) {
313 chunk.append(lexeme());
314 readToken();
315 }
316 defaultValue = chunk.toString();
317 }
318 append(type, name, defaultValue);
319 return true;
320}
321
322/*!
323 This function uses a Tokenizer to parse the \a signature,
324 which is a comma-separated list of parameter declarations.
325 If an error is detected, the Parameters object is cleared
326 and \c false is returned. Otherwise \c true is returned.
327 */
328bool Parameters::parse(const QString &signature)
329{
330 Tokenizer *outerTokenizer = m_tokenizer;
331 int outerTok = m_tok;
332
333 const QByteArray &latin1 = signature.toLatin1();
334 Tokenizer stringTokenizer(Location(), latin1);
335 stringTokenizer.setParsingFnOrMacro(true);
336 m_tokenizer = &stringTokenizer;
337
338 readToken();
339 do {
340 if (!matchParameter()) {
341 m_parameters.clear();
342 m_valid = false;
343 break;
344 }
345 } while (match(Tok_Comma));
346
347 m_tokenizer = outerTokenizer;
348 m_tok = outerTok;
349 return m_valid;
350}
351
352/*!
353 Append a Parameter constructed from \a type, \a name, and \a value
354 to the parameter vector.
355 */
356void Parameters::append(const QString &type, const QString &name, const QString &value)
357{
358 m_parameters.append(Parameter(type, name, value));
359}
360
361/*!
362 Returns the list of reconstructed parameters. If \a includeValues
363 is true, the default values are included, if any are present.
364 */
365QString Parameters::signature(bool includeValues) const
366{
367 QString result;
368 if (!m_parameters.empty()) {
369 for (int i = 0; i < m_parameters.size(); i++) {
370 if (i > 0)
371 result += ", ";
372 result += m_parameters.at(i).signature(includeValues);
373 }
374 }
375 return result;
376}
377
378/*!
379 Returns the signature of all the parameters with all the
380 spaces and commas removed. It is unintelligible, but that
381 is what the caller wants.
382
383 If \a names is true, the parameter names are included. If
384 \a values is true, the default values are included.
385 */
386QString Parameters::rawSignature(bool names, bool values) const
387{
388 QString raw;
389 const auto params = m_parameters;
390 for (const auto &parameter : params) {
391 raw += parameter.type();
392 if (names)
393 raw += parameter.name();
394 if (values)
395 raw += parameter.defaultValue();
396 }
397 return raw;
398}
399
400/*!
401 Parse the parameter \a signature by splitting the string,
402 and store the individual parameters in the parameter vector.
403
404 This method of parsing is naive but sufficient for QML methods
405 and macros.
406 */
407void Parameters::set(const QString &signature)
408{
409 clear();
410 if (!signature.isEmpty()) {
411 QStringList commaSplit = signature.split(',');
412 m_parameters.resize(commaSplit.size());
413 int i = 0;
414 for (const auto &item : std::as_const(commaSplit)) {
415 QStringList blankSplit = item.split(' ', Qt::SkipEmptyParts);
416 QString pDefault;
417 qsizetype defaultIdx = blankSplit.indexOf(QStringLiteral("="));
418 if (defaultIdx != -1) {
419 if (++defaultIdx < blankSplit.size())
420 pDefault = blankSplit.mid(defaultIdx).join(' ');
421 blankSplit = blankSplit.mid(0, defaultIdx - 1);
422 }
423 QString pName = blankSplit.takeLast();
424 QString pType = blankSplit.join(' ');
425 if (pType.isEmpty() && pName == QLatin1String("..."))
426 qSwap(pType, pName);
427 else {
428 int j = 0;
429 while (j < pName.size() && !pName.at(j).isLetter())
430 j++;
431 if (j > 0) {
432 pType += QChar(' ') + pName.left(j);
433 pName = pName.mid(j);
434 }
435 }
436 m_parameters[i++].set(pType, pName, pDefault);
437 }
438 }
439}
440
441/*!
442 Insert all the parameter names into names.
443 */
445{
446 QSet<QString> names;
447 const auto params = m_parameters;
448 for (const auto &parameter : params) {
449 if (!parameter.name().isEmpty())
450 names.insert(parameter.name());
451 }
452 return names;
453}
454
455/*!
456 Construct a list of the parameter types and return it.
457 */
459{
460 QString out;
461 if (count() > 0) {
462 for (int i = 0; i < count(); ++i) {
463 if (i > 0)
464 out += ", ";
465 out += m_parameters.at(i).type();
466 }
467 }
468 return out;
469}
470
471/*!
472 Construct a list of the parameter type/name pairs and
473 return it.
474*/
476{
477 QString out;
478 if (count() > 0) {
479 for (int i = 0; i < count(); ++i) {
480 if (i != 0)
481 out += ", ";
482 const Parameter &p = m_parameters.at(i);
483 out += p.type();
484 if (out[out.size() - 1].isLetterOrNumber())
485 out += QLatin1Char(' ');
486 out += p.name();
487 }
488 }
489 return out;
490}
491
492/*!
493 Construct a list of just the parameter names (without types)
494 and return it. Used for generating code snippets where only
495 argument names are needed. If a parameter has no name, a
496 synthetic name (arg1, arg2, etc.) is generated.
497*/
499{
500 const int n = count();
501 if (n == 0)
502 return {};
503
504 QString out;
505
506 for (int i = 0; i < n; ++i) {
507 if (i != 0)
508 out += ", ";
509 const QString &name = m_parameters.at(i).name();
510 if (name.isEmpty())
511 out += QString("arg%1").arg(i + 1);
512 else
513 out += name;
514 }
515 return out;
516}
517
518/*!
519 Returns true if \a parameters contains the same parameter
520 signature as this.
521 */
522bool Parameters::match(const Parameters &parameters) const
523{
524 return std::equal(m_parameters.cbegin(), m_parameters.cend(), parameters.m_parameters.cbegin(),
525 parameters.m_parameters.cend(),
526 [](const Parameter &a, const Parameter &b) { return a.type() == b.type(); });
527}
528
529QT_END_NAMESPACE
The Location class provides a way to mark a location in a file.
Definition location.h:20
Location()
Constructs an empty location.
Definition location.cpp:48
The Parameter class describes one function parameter.
Definition parameter.h:14
\inmodule QtCore \reentrant
int getToken()
int parenDepth() const
Definition tokenizer.h:94
int bracketDepth() const
Definition tokenizer.h:95
void setParsingFnOrMacro(bool macro)
Definition tokenizer.h:88
A class for parsing and managing a function parameter list.
Definition main.cpp:28
QSet< QString > getNames() const
Insert all the parameter names into names.
bool match(const Parameters &parameters) const
Returns true if parameters contains the same parameter signature as this.
Parameters(const QString &signature)
QString rawSignature(bool names=false, bool values=false) const
Returns the signature of all the parameters with all the spaces and commas removed.
QString generateNameList() const
Construct a list of just the parameter names (without types) and return it.
QString generateTypeList() const
Construct a list of the parameter types and return it.
void clear()
Definition parameters.h:24
QString signature(bool includeValues=false) const
Returns the list of reconstructed parameters.
int count() const
Definition parameters.h:34
QString generateTypeAndNameList() const
Construct a list of the parameter type/name pairs and return it.
void append(const QString &type, const QString &name, const QString &value)
Append a Parameter constructed from type, name, and value to the parameter vector.
void set(const QString &signature)
Parse the parameter signature by splitting the string, and store the individual parameters in the par...
@ Tok_operator
Definition tokenizer.h:57
@ Tok_Eoi
Definition tokenizer.h:24
@ Tok_void
Definition tokenizer.h:62
@ Tok_int64
Definition tokenizer.h:64
@ Tok_unsigned
Definition tokenizer.h:61
@ Tok_RightParen
Definition tokenizer.h:29
@ Tok_Tilde
Definition tokenizer.h:43
@ Tok_char
Definition tokenizer.h:51
@ Tok_short
Definition tokenizer.h:58
@ Tok_const
Definition tokenizer.h:53
@ Tok_Ellipsis
Definition tokenizer.h:39
@ Tok_Gulbrandsen
Definition tokenizer.h:40
@ Tok_Equal
Definition tokenizer.h:31
@ Tok_int
Definition tokenizer.h:55
@ Tok_long
Definition tokenizer.h:56
@ Tok_Caret
Definition tokenizer.h:27
@ Tok_LeftBracket
Definition tokenizer.h:41
@ Tok_LeftAngle
Definition tokenizer.h:36
@ Tok_Comma
Definition tokenizer.h:38
@ Tok_LeftBrace
Definition tokenizer.h:32
@ Tok_Ident
Definition tokenizer.h:49
@ Tok_LeftParenAster
Definition tokenizer.h:30
@ Tok_volatile
Definition tokenizer.h:63
@ Tok_double
Definition tokenizer.h:54
@ Tok_LeftParen
Definition tokenizer.h:28
@ Tok_RightAngle
Definition tokenizer.h:37
@ Tok_RightBrace
Definition tokenizer.h:33
@ Tok_Ampersand
Definition tokenizer.h:25
@ Tok_RightBracket
Definition tokenizer.h:42
@ Tok_Aster
Definition tokenizer.h:26
@ Tok_signed
Definition tokenizer.h:59
@ Tok_Comment
Definition tokenizer.h:48
@ Tok_QPrivateSignal
Definition tokenizer.h:65