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