11using namespace Qt::StringLiterals;
12using namespace QQmlJS::AST;
16
17
18
19
20
21
23LinterVisitor::LinterVisitor(
24 QQmlJSImporter *importer, QQmlJSLogger *logger,
25 const QString &implicitImportDirectory,
const QStringList &qmldirFiles,
26 QQmlJS::Engine *engine)
27 : QQmlJSImportVisitor(importer, logger, implicitImportDirectory, qmldirFiles)
34 const auto leaveEnv = qScopeGuard([
this] { QQmlJSImportVisitor::leaveEnvironment(); });
36 if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope)
39 if (
auto base = m_currentScope->baseType()) {
40 if (base->internalName() == u"QQmlComponent"_s) {
41 const auto nChildren = std::count_if(
42 m_currentScope->childScopesBegin(), m_currentScope->childScopesEnd(),
43 [](
const QQmlJSScope::ConstPtr &scope) {
44 return scope->scopeType() == QQmlSA::ScopeType::QMLScope;
47 m_logger->log(
"Components must have exactly one child"_L1,
48 qmlComponentChildrenCount, m_currentScope->sourceLocation());
54bool LinterVisitor::
visit(StringLiteral *sl)
56 QQmlJSImportVisitor::visit(sl);
57 const QString s = m_logger->code().mid(sl->literalToken.begin(), sl->literalToken.length);
59 if (s.contains(QLatin1Char(
'\r')) || s.contains(QLatin1Char(
'\n')) || s.contains(QChar(0x2028u))
60 || s.contains(QChar(0x2029u))) {
61 QString templateString;
64 const QChar stringQuote = s[0];
65 for (qsizetype i = 1; i < s.size() - 1; i++) {
73 templateString.chop(1);
78 templateString += u'\\';
79 if (c == u'$' && i + 1 < s.size() - 1 && s[i + 1] == u'{')
80 templateString += u'\\';
87 u"`" % templateString % u"`" };
88 suggestion.setAutoApplicable();
89 m_logger->log(QStringLiteral(
"String contains unescaped line terminator which is "
91 qmlMultilineStrings, sl->literalToken,
true,
true, suggestion);
98 m_ancestryIncludingCurrentNode.push_back(n);
104 Q_ASSERT(m_ancestryIncludingCurrentNode.back() == n);
105 m_ancestryIncludingCurrentNode.pop_back();
110 if (m_ancestryIncludingCurrentNode.size() < 2)
112 return m_ancestryIncludingCurrentNode[m_ancestryIncludingCurrentNode.size() - 2];
115bool LinterVisitor::
visit(CommaExpression *expression)
117 QQmlJSImportVisitor::visit(expression);
118 if (!expression->left || !expression->right)
122 if (cast<ForStatement *>(astParentOfVisitedNode()))
125 m_logger->log(
"Do not use comma expressions."_L1, qmlComma, expression->commaToken);
131 static constexpr std::
array literals{
"Boolean"_L1,
"Function"_L1,
"JSON"_L1,
132 "Math"_L1,
"Number"_L1,
"String"_L1 };
144bool LinterVisitor::
visit(NewMemberExpression *expression)
146 QQmlJSImportVisitor::visit(expression);
151bool LinterVisitor::
visit(VoidExpression *ast)
153 QQmlJSImportVisitor::visit(ast);
154 m_logger->log(
"Do not use void expressions."_L1, qmlVoid, ast->voidToken);
160 Q_ASSERT(exp->op == QSOperator::Add);
162 SourceLocation location = exp->operatorToken;
165 if (
auto increment = cast<PostIncrementExpression *>(exp->left))
166 location = combine(increment->incrementToken, location);
168 if (
auto unary = cast<UnaryPlusExpression *>(exp->right))
169 location = combine(location, unary->plusToken);
171 if (
auto increment = cast<PreIncrementExpression *>(exp->right))
172 location = combine(location, increment->incrementToken);
174 if (location == exp->operatorToken)
175 return SourceLocation{};
182 Q_ASSERT(exp->op == QSOperator::Sub);
184 SourceLocation location = exp->operatorToken;
187 if (
auto decrement = cast<PostDecrementExpression *>(exp->left))
188 location = combine(decrement->decrementToken, location);
190 if (
auto unary = cast<UnaryMinusExpression *>(exp->right))
191 location = combine(location, unary->minusToken);
193 if (
auto decrement = cast<PreDecrementExpression *>(exp->right))
194 location = combine(location, decrement->decrementToken);
196 if (location == exp->operatorToken)
197 return SourceLocation{};
202bool LinterVisitor::
visit(BinaryExpression *exp)
204 QQmlJSImportVisitor::visit(exp);
206 case QSOperator::Add:
207 if (SourceLocation loc = confusingPluses(exp); loc.isValid())
208 m_logger->log(
"Confusing pluses."_L1, qmlConfusingPluses, loc);
210 case QSOperator::Sub:
211 if (SourceLocation loc = confusingMinuses(exp); loc.isValid())
212 m_logger->log(
"Confusing minuses."_L1, qmlConfusingMinuses, loc);
221bool LinterVisitor::
visit(QQmlJS::AST::UiImport *import)
223 QQmlJSImportVisitor::visit(import);
225 const auto locAndName = [](
const UiImport *i) {
227 return std::make_pair(i->fileNameToken, i->fileName.toString());
229 QQmlJS::SourceLocation l = i->importUri->firstSourceLocation();
230 if (i->importIdToken.isValid())
231 l = combine(l, i->importIdToken);
233 l = combine(l, i->version->minorToken);
235 l = combine(l, i->importUri->lastSourceLocation());
237 return std::make_pair(l, i->importUri->toString());
240 SeenImport i(import);
241 if (
const auto it = m_seenImports.constFind(i); it != m_seenImports.constEnd()) {
242 const auto locAndNameImport = locAndName(import);
243 const auto locAndNameSeen = locAndName(it->uiImport);
244 m_logger->log(
"Duplicate import '%1'"_L1.arg(locAndNameImport.second),
245 qmlDuplicateImport, locAndNameImport.first);
246 m_logger->log(
"Note: previous import '%1' here"_L1.arg(locAndNameSeen.second),
247 qmlDuplicateImport, locAndNameSeen.first,
true,
true, {},
248 locAndName(import).first.startLine);
251 m_seenImports.insert(i);
255void LinterVisitor::handleDuplicateEnums(UiEnumMemberList *members, QStringView key,
256 const QQmlJS::SourceLocation &location)
258 m_logger->log(u"Enum key '%1' has already been declared"_s.arg(key), qmlDuplicateEnumEntries,
260 for (
const auto *member = members; member; member = member->next) {
261 if (member->member.toString() == key) {
262 m_logger->log(u"Note: previous declaration of '%1' here"_s.arg(key),
263 qmlDuplicateEnumEntries, member->memberToken);
269bool LinterVisitor::
visit(QQmlJS::AST::UiEnumDeclaration *uied)
271 QQmlJSImportVisitor::visit(uied);
273 if (m_currentScope->isInlineComponent()) {
274 m_logger->log(u"Enums declared inside of inline component are ignored."_s,
275 qmlInlineComponentEnums, uied->firstSourceLocation());
276 }
else if (m_currentScope->componentRootStatus() == QQmlJSScope::IsComponentRoot::No
277 && !m_currentScope->isFileRootComponent()) {
278 m_logger->log(u"Enum declared outside the root element. It won't be accessible."_s,
279 qmlNonRootEnums, uied->firstSourceLocation());
282 QHash<QStringView,
const QQmlJS::AST::UiEnumMemberList *> seen;
283 for (
const auto *member = uied->members; member; member = member->next) {
284 QStringView key = member->member;
285 if (!key.front().isUpper()) {
286 m_logger->log(u"Enum keys should start with an uppercase."_s, qmlEnumKeyCase,
287 member->memberToken);
290 if (seen.contains(key))
291 handleDuplicateEnums(uied->members, key, member->memberToken);
293 seen[member->member] = member;
295 if (uied->name == key) {
296 m_logger->log(
"Enum entry should be named differently than the enum itself to avoid "
297 "confusion."_L1, qmlEnumEntryMatchesEnum, member->firstSourceLocation());
310 switch (statement->kind) {
311 case Node::Kind_Block: {
312 return allCodePathsReturnInsideCase(cast<Block *>(statement)->statements);
314 case Node::Kind_BreakStatement:
316 case Node::Kind_CaseBlock: {
317 const CaseBlock *caseBlock = cast<CaseBlock *>(statement);
318 if (caseBlock->defaultClause)
319 return allCodePathsReturnInsideCase(caseBlock->defaultClause);
320 return allCodePathsReturnInsideCase(caseBlock->clauses);
322 case Node::Kind_CaseClause:
323 return allCodePathsReturnInsideCase(cast<CaseClause *>(statement)->statements);
324 case Node::Kind_CaseClauses: {
325 for (CaseClauses *caseClauses = cast<CaseClauses *>(statement); caseClauses;
326 caseClauses = caseClauses->next) {
327 if (!allCodePathsReturnInsideCase(caseClauses->clause))
332 case Node::Kind_ContinueStatement:
336 case Node::Kind_DefaultClause:
337 return allCodePathsReturnInsideCase(cast<DefaultClause *>(statement)->statements);
338 case Node::Kind_IfStatement: {
339 const auto *ifStatement = cast<IfStatement *>(statement);
340 return allCodePathsReturnInsideCase(ifStatement->ok)
341 && allCodePathsReturnInsideCase(ifStatement->ko);
343 case Node::Kind_LabelledStatement:
344 return allCodePathsReturnInsideCase(cast<LabelledStatement *>(statement)->statement);
345 case Node::Kind_ReturnStatement:
347 case Node::Kind_StatementList: {
348 for (StatementList *list = cast<StatementList *>(statement); list; list = list->next) {
349 if (allCodePathsReturnInsideCase(list->statement))
354 case Node::Kind_SwitchStatement:
355 return allCodePathsReturnInsideCase(cast<SwitchStatement *>(statement)->block);
356 case Node::Kind_ThrowStatement:
358 case Node::Kind_TryStatement: {
359 auto *tryStatement = cast<TryStatement *>(statement);
360 if (allCodePathsReturnInsideCase(tryStatement->statement))
362 return allCodePathsReturnInsideCase(tryStatement->finallyExpression->statement);
364 case Node::Kind_WithStatement:
365 return allCodePathsReturnInsideCase(cast<WithStatement *>(statement)->statement);
372void LinterVisitor::checkCaseFallthrough(StatementList *statements, SourceLocation errorLoc,
373 SourceLocation nextLoc)
375 if (!statements || !nextLoc.isValid())
378 if (allCodePathsReturnInsideCase(statements))
381 quint32 afterLastStatement = 0;
382 for (StatementList *it = statements; it; it = it->next) {
384 afterLastStatement = it->statement->lastSourceLocation().end();
388 const auto &comments = m_engine->comments();
389 auto it = std::find_if(comments.cbegin(), comments.cend(),
390 [&](
auto c) {
return afterLastStatement < c.offset; });
391 auto end = std::find_if(it, comments.cend(),
392 [&](
auto c) {
return c.offset >= nextLoc.offset; });
394 for (; it != end; ++it) {
395 const QString &commentText = m_engine->code().mid(it->offset, it->length);
396 if (commentText.contains(
"fall through"_L1)
397 || commentText.contains(
"fall-through"_L1)
398 || commentText.contains(
"fallthrough"_L1)) {
404 "Non-empty case block potentially falls through to the next case or default statement. "
405 "Add \"// fallthrough\" at the end of the block to silence this warning."_L1,
406 qmlUnterminatedCase, errorLoc);
409bool LinterVisitor::
visit(QQmlJS::AST::CaseBlock *block)
411 QQmlJSImportVisitor::visit(block);
413 std::vector<std::pair<SourceLocation, StatementList *>> clauses;
414 for (CaseClauses *it = block->clauses; it; it = it->next)
415 clauses.push_back({ it->clause->caseToken, it->clause->statements });
416 if (block->defaultClause)
417 clauses.push_back({ block->defaultClause->defaultToken, block->defaultClause->statements });
418 for (CaseClauses *it = block->moreClauses; it; it = it->next)
419 clauses.push_back({ it->clause->caseToken, it->clause->statements });
422 for (size_t i = 0; i < clauses.size() - 1; ++i) {
423 const SourceLocation nextToken = clauses[i + 1].first;
424 checkCaseFallthrough(clauses[i].second, clauses[i].first, nextToken);
430
431
432
433
437 case Node::Kind_CallExpression:
438 case Node::Kind_DeleteExpression:
439 case Node::Kind_NewExpression:
440 case Node::Kind_PreDecrementExpression:
441 case Node::Kind_PreIncrementExpression:
442 case Node::Kind_PostDecrementExpression:
443 case Node::Kind_PostIncrementExpression:
444 case Node::Kind_YieldExpression:
445 case Node::Kind_FunctionExpression:
450 BinaryExpression *binary = cast<BinaryExpression *>(ast);
454 switch (binary->op) {
455 case QSOperator::InplaceAnd:
456 case QSOperator::Assign:
457 case QSOperator::InplaceSub:
458 case QSOperator::InplaceDiv:
459 case QSOperator::InplaceExp:
460 case QSOperator::InplaceAdd:
461 case QSOperator::InplaceLeftShift:
462 case QSOperator::InplaceMod:
463 case QSOperator::InplaceMul:
464 case QSOperator::InplaceOr:
465 case QSOperator::InplaceRightShift:
466 case QSOperator::InplaceURightShift:
467 case QSOperator::InplaceXor:
472 Q_UNREACHABLE_RETURN(
true);
477 return parent->kind != Node::Kind_UiScriptBinding && parent->kind != Node::Kind_UiPublicMember;
480bool LinterVisitor::
visit(ExpressionStatement *ast)
482 QQmlJSImportVisitor::visit(ast);
484 if (canHaveUselessExpressionStatement(astParentOfVisitedNode())
485 && isUselessExpressionStatement(ast->expression)) {
486 m_logger->log(
"Expression statement has no obvious effect."_L1,
487 qmlConfusingExpressionStatement,
488 combine(ast->firstSourceLocation(), ast->lastSourceLocation()));
494bool LinterVisitor::
safeInsertJSIdentifier(QQmlJSScope::Ptr &scope,
const QString &name,
const QQmlJSScope::JavaScriptIdentifier &identifier)
496 if (scope->scopeType() == QQmlSA::ScopeType::JSLexicalScope &&
497 identifier.kind == QQmlJSScope::JavaScriptIdentifier::FunctionScoped) {
500 Q_ASSERT(!scope->parentScope().isNull());
501 auto parentScopeType = scope->parentScope()->scopeType();
504 if (!inTopLevelBindingBlockScope) {
505 m_logger->log(u"var declaration in block scope is hoisted to function scope\n"_s
506 u"Replace it with const or let to silence the warning\n"_s,
507 qmlBlockScopeVarDeclaration, identifier.location);
510 const QQmlJSScope *scopePtr = scope.get();
511 std::pair<
const QQmlJSScope*, QString> misplaced { scopePtr, name };
512 if (misplacedJSIdentifiers.contains(misplaced))
514 misplacedJSIdentifiers.insert(misplaced);
515 m_logger->log(u"JavaScript declarations are not allowed in QML elements"_s, qmlSyntax,
516 identifier.location);
519 return QQmlJSImportVisitor::safeInsertJSIdentifier(scope, name, identifier);
523 const QString &name,
const QQmlJS::AST::Statement *statement,
524 const QQmlJS::AST::UiPublicMember *associatedPropertyDefinition)
526 if (statement && statement->kind == (
int)AST::Node::Kind::Kind_Block) {
527 const auto *block =
static_cast<
const AST::Block *>(statement);
528 if (!block->statements && associatedPropertyDefinition) {
529 m_logger->log(
"Unintentional empty block, use ({}) for empty object literal"_L1,
530 qmlUnintentionalEmptyBlock,
531 combine(block->lbraceToken, block->rbraceToken));
535 return QQmlJSImportVisitor::parseBindingExpression(name, statement, associatedPropertyDefinition);
539 const UiPublicMember *associatedPropertyDefinition)
541 if (!m_currentScope->hasOwnProperty(binding.propertyName()))
544 if (!associatedPropertyDefinition->isReadonly())
547 const auto &prop = m_currentScope->property(binding.propertyName());
548 const auto log = [&](
const QString &preferredType) {
549 m_logger->log(
"Prefer more specific type %1 over var"_L1.arg(preferredType),
550 qmlPreferNonVarProperties, prop.sourceLocation());
553 if (prop.typeName() !=
"QVariant"_L1)
556 switch (binding.bindingType()) {
562 double v = binding.numberValue();
563 auto loc = binding.sourceLocation();
564 QStringView literal = QStringView(m_engine->code()).mid(loc.offset, loc.length);
565 if (literal.contains(u'.') ||
double(
int(v)) != v)
566 log(
"real or double"_L1);
583 QQmlJSImportVisitor::endVisit(ast);
595 const QQmlJS::SourceLocation &location,
596 QQmlJSLogger *logger)
599 if (!base->hasMethod(name))
602 static constexpr QLatin1String warningMessage =
603 "%1 \"%2\" already exists in base type \"%3\", use a different name."_L1;
604 const auto owner = QQmlJSScope::ownerOfMethod(base, name).scope;
606 logger->log(warningMessage.arg(isSignal ?
"Signal"_L1 :
"Method"_L1, name,
607 QQmlJSUtils::getScopeName(owner, QQmlSA::ScopeType::QMLScope)),
608 qmlShadow, location);
612 const QQmlJS::SourceLocation &location,
613 OverrideInformations overrideFlags, QQmlJSLogger *logger)
616 const bool hasOverride = overrideFlags.testFlag(
WithOverride);
617 if (!base->hasProperty(name)) {
621 "Member \"%1\" does not override anything. Consider removing \"override\"."_L1.arg(
623 qmlPropertyOverride, location);
627 const auto owner = QQmlJSScope::ownerOfProperty(base, name).scope;
628 const auto shadowedProperty = owner->ownProperty(name);
629 if (shadowedProperty.isFinal()) {
632 ?
"Member \"%1\" shadows final member \"%1\" from base type \"%2\", use a different name."_L1
633 :
"Member \"%1\" overrides final member \"%1\" from base type \"%2\", use a different name and remove the \"override\"."_L1)
634 .arg(name, QQmlJSUtils::getScopeName(owner, QQmlSA::ScopeType::QMLScope)),
635 qmlPropertyOverride, location);
639 if (shadowedProperty.isVirtual() || shadowedProperty.isOverride()) {
640 if (hasOverride || overrideFlags.testFlag(
WithFinal))
644 "Member \"%1\" shadows member \"%1\" from base type \"%2\", use a different name or add a final or override specifier."_L1
645 .arg(name, QQmlJSUtils::getScopeName(owner, QQmlSA::ScopeType::QMLScope)),
646 qmlPropertyOverride, location);
652 "Member \"%1\" overrides a non-virtual member from base type \"%2\", use a different name or mark the property as virtual in the base type."_L1
653 .arg(name, QQmlJSUtils::getScopeName(owner, QQmlSA::ScopeType::QMLScope)),
654 qmlPropertyOverride, location);
657 logger->log(
"Property \"%2\" already exists in base type \"%3\", use a different name."_L1.arg(
658 name, QQmlJSUtils::getScopeName(owner, QQmlSA::ScopeType::QMLScope)),
659 qmlPropertyOverride, location);
663 QLatin1String type,
const QQmlJS::SourceLocation &location,
664 OverrideInformations overrideFlags, QQmlJSLogger *logger)
666 static constexpr QLatin1String duplicateMessage =
667 "Duplicated %1 name \"%2\", \"%2\" is already a %3."_L1;
668 if (
const auto methods = scope->ownMethods(name); !methods.isEmpty()) {
669 logger->log(duplicateMessage.arg(type, name,
673 qmlDuplicatedName, location);
675 if (scope->hasOwnProperty(name))
676 logger->log(duplicateMessage.arg(type, name, s_property), qmlDuplicatedName, location);
678 const QQmlJSScope::ConstPtr base = scope->baseType();
682 warnForMethodShadowingInBase(base, name, location, logger);
683 warnForPropertyShadowingInBase(base, name, location, overrideFlags, logger);
686bool LinterVisitor::
visit(UiPublicMember *publicMember)
688 switch (publicMember->type) {
689 case UiPublicMember::Signal: {
690 const QString signalName = publicMember->name.toString();
691 warnForDuplicates(m_currentScope, signalName, s_signal, publicMember->identifierToken,
695 case QQmlJS::AST::UiPublicMember::Property: {
696 const QString propertyName = publicMember->name.toString();
697 OverrideInformations flags;
698 flags.setFlag(
WithOverride, publicMember->isOverride());
699 flags.setFlag(
WithFinal, publicMember->isFinal());
700 warnForDuplicates(m_currentScope, propertyName, s_property, publicMember->identifierToken,
705 return QQmlJSImportVisitor::visit(publicMember);
708bool LinterVisitor::
visit(FunctionExpression *fexpr)
710 if (m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope) {
711 warnForDuplicates(m_currentScope, fexpr->name.toString(), s_method, fexpr->identifierToken,
714 return QQmlJSImportVisitor::visit(fexpr);
717bool LinterVisitor::
visit(FunctionDeclaration *fdecl)
719 if (m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope) {
720 warnForDuplicates(m_currentScope, fdecl->name.toString(), s_method, fdecl->identifierToken,
723 return QQmlJSImportVisitor::visit(fdecl);
728 const QQmlJSScope::ConstPtr ¤tScope,
730 QQmlJSLogger *logger)
732 static constexpr QLatin1String warningMessage =
733 "Id \"%1\" shadows %2 \"%1\"%3. Rename the id or the %2."_L1;
735 if (mode ==
Property ? !currentScope->hasProperty(name) : !currentScope->hasMethod(name))
738 const auto owner = mode == Property ? QQmlJSScope::ownerOfProperty(currentScope, name).scope
739 : QQmlJSScope::ownerOfMethod(currentScope, name).scope;
740 const QString currentScopeName =
741 QQmlJSUtils::getScopeName(currentScope, QQmlSA::ScopeType::QMLScope);
743 const QLatin1String memberType = mode == Property
745 : (owner->methods(name).front().methodType() == QQmlJSMetaMethodType::Signal
749 const QQmlJS::SourceLocation definitionLocation = mode ==
Property
750 ? currentScope->property(name).sourceLocation()
751 : currentScope->methods(name).front().sourceLocation();
752 auto log = [&](
const QString &asdf) {
753 logger->log(warningMessage.arg(name, memberType, asdf), qmlIdShadowsMember, location);
756 if (owner->filePath() == scopeWithId->filePath()) {
757 logger->log(
"Note: %1 \"%2\" defined here is shadowed by id \"%2\""_L1.arg(memberType,
759 qmlIdShadowsMember, definitionLocation,
true,
true, {}, location.startLine);
762 "Note: type \"%1\" defined here has a %2 \"%3\" shadowed by id \"%3\""_L1.arg(
763 currentScopeName, memberType, name),
764 qmlIdShadowsMember, currentScope->sourceLocation(),
true,
true, {},
769 if (currentScope != scopeWithId) {
770 log(
" from \"%1\" defined at %2:%3:%4"_L1.arg(
771 currentScopeName, currentScope->filePath(),
772 QString::number(currentScope->sourceLocation().startLine),
773 QString::number(currentScope->sourceLocation().startColumn)));
776 log(
" from current type"_L1);
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799void LinterVisitor::checkIdShadows()
801 const auto componentRootsToIds = m_scopesById.computeComponentRootsToIds();
802 if (componentRootsToIds.empty())
805 using It =
decltype(componentRootsToIds.begin());
806 auto begin = componentRootsToIds.begin();
807 auto end = componentRootsToIds.end();
808 auto nextKey = [&](It it) -> It {
809 return it == end ? it : componentRootsToIds.upper_bound(it->first);
812 for (
auto it = begin, it2 = nextKey(begin); it != end; it = std::exchange(it2, nextKey(it2))) {
813 std::stack<QQmlJSScope::ConstPtr> stack{ { it->first } };
814 while (!stack.empty()) {
815 QQmlJSScope::ConstPtr current = stack.top();
818 for (
auto scopeWithIdIt = it, scopeWithIdEnd = it2; scopeWithIdIt != scopeWithIdEnd;
820 warnForShadowsInCurrentScope(scopeWithIdIt->second.scope, scopeWithIdIt->second.id,
822 scopeWithIdIt->second.scope->idSourceLocation(),
824 warnForShadowsInCurrentScope(scopeWithIdIt->second.scope, scopeWithIdIt->second.id,
826 scopeWithIdIt->second.scope->idSourceLocation(),
829 const auto children = current->childScopes();
830 for (
const QQmlJSScope::ConstPtr &child : children)
void postVisit(QQmlJS::AST::Node *) override
void handleLiteralBinding(const QQmlJSMetaPropertyBinding &binding, const AST::UiPublicMember *associatedPropertyDefinition) override
BindingExpressionParseResult parseBindingExpression(const QString &name, const QQmlJS::AST::Statement *statement, const QQmlJS::AST::UiPublicMember *associatedPropertyDefinition=nullptr) override
void leaveEnvironment() override
bool safeInsertJSIdentifier(QQmlJSScope::Ptr &scope, const QString &name, const QQmlJSScope::JavaScriptIdentifier &identifier) override
void endVisit(QQmlJS::AST::UiProgram *ast) override
bool visit(QQmlJS::AST::StringLiteral *) override
QQmlJS::AST::Node * astParentOfVisitedNode() const
bool preVisit(QQmlJS::AST::Node *) override
static void warnForMethodShadowingInBase(const QQmlJSScope::ConstPtr &base, const QString &name, const QQmlJS::SourceLocation &location, QQmlJSLogger *logger)
static bool isUselessExpressionStatement(ExpressionNode *ast)
static void warnForDuplicates(const QQmlJSScope::ConstPtr &scope, const QString &name, QLatin1String type, const QQmlJS::SourceLocation &location, OverrideInformations overrideFlags, QQmlJSLogger *logger)
static constexpr QLatin1String s_method
static void warnAboutLiteralConstructors(NewMemberExpression *expression, QQmlJSLogger *logger)
static SourceLocation confusingPluses(BinaryExpression *exp)
static bool allCodePathsReturnInsideCase(Node *statement)
Q_DECLARE_FLAGS(OverrideInformations, OverrideInformation)
static SourceLocation confusingMinuses(BinaryExpression *exp)
void warnForShadowsInCurrentScope(const QQmlJSScope::ConstPtr &scopeWithId, const QString &name, const QQmlJSScope::ConstPtr ¤tScope, const QQmlJS::SourceLocation &location, MethodOrProperty mode, QQmlJSLogger *logger)
static constexpr QLatin1String s_property
static constexpr QLatin1String s_signal
static bool canHaveUselessExpressionStatement(Node *parent)
static void warnForPropertyShadowingInBase(const QQmlJSScope::ConstPtr &base, const QString &name, const QQmlJS::SourceLocation &location, OverrideInformations overrideFlags, QQmlJSLogger *logger)
@ SignalHandlerFunctionScope