CPlusPlus: Support structured bindings

While we do recommend clangd for modern code bases, we should still be
able to parse basic language constructs.

Fixes: QTCREATORBUG-27975
Change-Id: I189b991685a5cd5f62f2afce77878b60c895e8f9
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2022-08-17 18:10:53 +02:00
parent 5fab54d95a
commit ca00b874a7
17 changed files with 225 additions and 43 deletions

View File

@@ -4634,4 +4634,3 @@ int NoExceptOperatorExpressionAST::lastToken() const
return noexcept_token + 1; return noexcept_token + 1;
return 1; return 1;
} }

View File

@@ -210,6 +210,7 @@ public:
virtual DeclarationStatementAST *asDeclarationStatement() { return nullptr; } virtual DeclarationStatementAST *asDeclarationStatement() { return nullptr; }
virtual DeclaratorAST *asDeclarator() { return nullptr; } virtual DeclaratorAST *asDeclarator() { return nullptr; }
virtual DeclaratorIdAST *asDeclaratorId() { return nullptr; } virtual DeclaratorIdAST *asDeclaratorId() { return nullptr; }
virtual DecompositionDeclaratorAST *asDecompositionDeclarator() { return nullptr; }
virtual DecltypeSpecifierAST *asDecltypeSpecifier() { return nullptr; } virtual DecltypeSpecifierAST *asDecltypeSpecifier() { return nullptr; }
virtual DeleteExpressionAST *asDeleteExpression() { return nullptr; } virtual DeleteExpressionAST *asDeleteExpression() { return nullptr; }
virtual DesignatedInitializerAST *asDesignatedInitializer() { return nullptr; } virtual DesignatedInitializerAST *asDesignatedInitializer() { return nullptr; }
@@ -1277,6 +1278,24 @@ protected:
bool match0(AST *, ASTMatcher *) override; bool match0(AST *, ASTMatcher *) override;
}; };
class CPLUSPLUS_EXPORT DecompositionDeclaratorAST: public CoreDeclaratorAST
{
public:
NameListAST *identifiers = nullptr;
public:
DecompositionDeclaratorAST *asDecompositionDeclarator() override { return this; }
int firstToken() const override { return identifiers->firstToken(); }
int lastToken() const override { return identifiers->lastToken(); }
DecompositionDeclaratorAST *clone(MemoryPool *pool) const override;
protected:
void accept0(ASTVisitor *visitor) override;
bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT NestedDeclaratorAST: public CoreDeclaratorAST class CPLUSPLUS_EXPORT NestedDeclaratorAST: public CoreDeclaratorAST
{ {
public: public:

View File

@@ -1840,3 +1840,11 @@ DesignatedInitializerAST *DesignatedInitializerAST::clone(MemoryPool *pool) cons
return ast; return ast;
} }
DecompositionDeclaratorAST *DecompositionDeclaratorAST::clone(MemoryPool *pool) const
{
const auto theClone = new (pool) DecompositionDeclaratorAST;
for (NameListAST *iter = identifiers, **ast_iter = &theClone->identifiers;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
*ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : nullptr);
return theClone;
}

View File

@@ -352,6 +352,14 @@ bool DeclaratorIdAST::match0(AST *pattern, ASTMatcher *matcher)
return false; return false;
} }
bool DecompositionDeclaratorAST::match0(AST *pattern, ASTMatcher *matcher)
{
if (DecompositionDeclaratorAST *_other = pattern->asDecompositionDeclarator())
return matcher->match(this, _other);
return false;
}
bool NestedDeclaratorAST::match0(AST *pattern, ASTMatcher *matcher) bool NestedDeclaratorAST::match0(AST *pattern, ASTMatcher *matcher)
{ {
if (NestedDeclaratorAST *_other = pattern->asNestedDeclarator()) if (NestedDeclaratorAST *_other = pattern->asNestedDeclarator())

View File

@@ -836,6 +836,15 @@ bool ASTMatcher::match(DeclaratorIdAST *node, DeclaratorIdAST *pattern)
return true; return true;
} }
bool ASTMatcher::match(DecompositionDeclaratorAST *node, DecompositionDeclaratorAST *pattern)
{
if (!pattern->identifiers)
pattern->identifiers = node->identifiers;
else if (! AST::match(node->identifiers, pattern->identifiers, this))
return false;
return true;
}
bool ASTMatcher::match(NestedDeclaratorAST *node, NestedDeclaratorAST *pattern) bool ASTMatcher::match(NestedDeclaratorAST *node, NestedDeclaratorAST *pattern)
{ {
(void) node; (void) node;

View File

@@ -63,6 +63,7 @@ public:
virtual bool match(DeclarationStatementAST *node, DeclarationStatementAST *pattern); virtual bool match(DeclarationStatementAST *node, DeclarationStatementAST *pattern);
virtual bool match(DeclaratorAST *node, DeclaratorAST *pattern); virtual bool match(DeclaratorAST *node, DeclaratorAST *pattern);
virtual bool match(DeclaratorIdAST *node, DeclaratorIdAST *pattern); virtual bool match(DeclaratorIdAST *node, DeclaratorIdAST *pattern);
virtual bool match(DecompositionDeclaratorAST *node, DecompositionDeclaratorAST *pattern);
virtual bool match(DecltypeSpecifierAST *node, DecltypeSpecifierAST *pattern); virtual bool match(DecltypeSpecifierAST *node, DecltypeSpecifierAST *pattern);
virtual bool match(DeleteExpressionAST *node, DeleteExpressionAST *pattern); virtual bool match(DeleteExpressionAST *node, DeleteExpressionAST *pattern);
virtual bool match(DesignatedInitializerAST *node, DesignatedInitializerAST *pattern); virtual bool match(DesignatedInitializerAST *node, DesignatedInitializerAST *pattern);

View File

@@ -339,6 +339,13 @@ public:
return ast; return ast;
} }
DecompositionDeclaratorAST *DecompositionDeclarator(NameListAST *names = nullptr)
{
const auto ast = new (&pool) DecompositionDeclaratorAST;
ast->identifiers = names;
return ast;
}
NestedDeclaratorAST *NestedDeclarator(DeclaratorAST *declarator = nullptr) NestedDeclaratorAST *NestedDeclarator(DeclaratorAST *declarator = nullptr)
{ {
NestedDeclaratorAST *ast = new (&pool) NestedDeclaratorAST; NestedDeclaratorAST *ast = new (&pool) NestedDeclaratorAST;

View File

@@ -368,6 +368,14 @@ void DeclaratorIdAST::accept0(ASTVisitor *visitor)
visitor->endVisit(this); visitor->endVisit(this);
} }
void DecompositionDeclaratorAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
accept(identifiers, visitor);
}
visitor->endVisit(this);
}
void NestedDeclaratorAST::accept0(ASTVisitor *visitor) void NestedDeclaratorAST::accept0(ASTVisitor *visitor)
{ {
if (visitor->visit(this)) { if (visitor->visit(this)) {

View File

@@ -105,6 +105,7 @@ public:
virtual bool visit(DeclarationStatementAST *) { return true; } virtual bool visit(DeclarationStatementAST *) { return true; }
virtual bool visit(DeclaratorAST *) { return true; } virtual bool visit(DeclaratorAST *) { return true; }
virtual bool visit(DeclaratorIdAST *) { return true; } virtual bool visit(DeclaratorIdAST *) { return true; }
virtual bool visit(DecompositionDeclaratorAST *) { return true; }
virtual bool visit(DecltypeSpecifierAST *) { return true; } virtual bool visit(DecltypeSpecifierAST *) { return true; }
virtual bool visit(DeleteExpressionAST *) { return true; } virtual bool visit(DeleteExpressionAST *) { return true; }
virtual bool visit(DesignatedInitializerAST *) { return true; } virtual bool visit(DesignatedInitializerAST *) { return true; }
@@ -258,6 +259,7 @@ public:
virtual void endVisit(DeclarationStatementAST *) {} virtual void endVisit(DeclarationStatementAST *) {}
virtual void endVisit(DeclaratorAST *) {} virtual void endVisit(DeclaratorAST *) {}
virtual void endVisit(DeclaratorIdAST *) {} virtual void endVisit(DeclaratorIdAST *) {}
virtual void endVisit(DecompositionDeclaratorAST *) {}
virtual void endVisit(DecltypeSpecifierAST *) {} virtual void endVisit(DecltypeSpecifierAST *) {}
virtual void endVisit(DeleteExpressionAST *) {} virtual void endVisit(DeleteExpressionAST *) {}
virtual void endVisit(DesignatedInitializerAST *) {} virtual void endVisit(DesignatedInitializerAST *) {}

View File

@@ -66,6 +66,7 @@ class DeclarationAST;
class DeclarationStatementAST; class DeclarationStatementAST;
class DeclaratorAST; class DeclaratorAST;
class DeclaratorIdAST; class DeclaratorIdAST;
class DecompositionDeclaratorAST;
class DecltypeSpecifierAST; class DecltypeSpecifierAST;
class DeleteExpressionAST; class DeleteExpressionAST;
class DesignatedInitializerAST; class DesignatedInitializerAST;

View File

@@ -35,6 +35,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <utility>
using namespace CPlusPlus; using namespace CPlusPlus;
@@ -46,6 +47,7 @@ Bind::Bind(TranslationUnit *unit)
_expression(nullptr), _expression(nullptr),
_name(nullptr), _name(nullptr),
_declaratorId(nullptr), _declaratorId(nullptr),
_decompositionDeclarator(nullptr),
_visibility(Symbol::Public), _visibility(Symbol::Public),
_objcVisibility(Symbol::Public), _objcVisibility(Symbol::Public),
_methodKey(Function::NormalMethod), _methodKey(Function::NormalMethod),
@@ -341,7 +343,9 @@ bool Bind::visit(DeclaratorAST *ast)
return false; return false;
} }
FullySpecifiedType Bind::declarator(DeclaratorAST *ast, const FullySpecifiedType &init, DeclaratorIdAST **declaratorId) FullySpecifiedType Bind::declarator(DeclaratorAST *ast, const FullySpecifiedType &init,
DeclaratorIdAST **declaratorId,
DecompositionDeclaratorAST **decompDeclarator)
{ {
FullySpecifiedType type = init; FullySpecifiedType type = init;
@@ -349,6 +353,7 @@ FullySpecifiedType Bind::declarator(DeclaratorAST *ast, const FullySpecifiedType
return type; return type;
std::swap(_declaratorId, declaratorId); std::swap(_declaratorId, declaratorId);
std::swap(_decompositionDeclarator, decompDeclarator);
bool isAuto = false; bool isAuto = false;
const bool cxx11Enabled = translationUnit()->languageFeatures().cxx11Enabled; const bool cxx11Enabled = translationUnit()->languageFeatures().cxx11Enabled;
if (cxx11Enabled) if (cxx11Enabled)
@@ -380,6 +385,7 @@ FullySpecifiedType Bind::declarator(DeclaratorAST *ast, const FullySpecifiedType
} }
std::swap(_declaratorId, declaratorId); std::swap(_declaratorId, declaratorId);
std::swap(_decompositionDeclarator, decompDeclarator);
return type; return type;
} }
@@ -1997,14 +2003,25 @@ bool Bind::visit(SimpleDeclarationAST *ast)
for (DeclaratorListAST *it = ast->declarator_list; it; it = it->next) { for (DeclaratorListAST *it = ast->declarator_list; it; it = it->next) {
DeclaratorIdAST *declaratorId = nullptr; DeclaratorIdAST *declaratorId = nullptr;
FullySpecifiedType declTy = this->declarator(it->value, type, &declaratorId); DecompositionDeclaratorAST *decompDeclarator = nullptr;
FullySpecifiedType declTy = this->declarator(it->value, type, &declaratorId,
&decompDeclarator);
const Name *declName = nullptr; std::vector<std::pair<const Name *, int>> namesAndLocations;
int sourceLocation = location(it->value, ast->firstToken()); if (declaratorId && declaratorId->name) {
if (declaratorId && declaratorId->name) namesAndLocations.push_back({declaratorId->name->name,
declName = declaratorId->name->name; location(it->value, ast->firstToken())});
} else if (decompDeclarator) {
for (auto it = decompDeclarator->identifiers->begin();
it != decompDeclarator->identifiers->end(); ++it) {
if ((*it)->name)
namesAndLocations.push_back({(*it)->name, (*it)->firstToken()});
}
}
Declaration *decl = control()->newDeclaration(sourceLocation, declName); for (const auto &nameAndLoc : qAsConst(namesAndLocations)) {
const int sourceLocation = nameAndLoc.second;
Declaration *decl = control()->newDeclaration(sourceLocation, nameAndLoc.first);
decl->setType(declTy); decl->setType(declTy);
setDeclSpecifiers(decl, type); setDeclSpecifiers(decl, type);
@@ -2018,11 +2035,13 @@ bool Bind::visit(SimpleDeclarationAST *ast)
} }
else if (declTy.isAuto()) { else if (declTy.isAuto()) {
const ExpressionAST *initializer = it->value->initializer; const ExpressionAST *initializer = it->value->initializer;
if (!initializer && declaratorId) if (!initializer && declaratorId) {
translationUnit()->error(location(declaratorId->name, ast->firstToken()), "auto-initialized variable must have an initializer"); translationUnit()->error(location(declaratorId->name, ast->firstToken()),
else if (initializer) "auto-initialized variable must have an initializer");
} else if (initializer) {
decl->setInitializer(asStringLiteral(initializer)); decl->setInitializer(asStringLiteral(initializer));
} }
}
if (_scope->asClass()) { if (_scope->asClass()) {
decl->setVisibility(_visibility); decl->setVisibility(_visibility);
@@ -2043,6 +2062,7 @@ bool Bind::visit(SimpleDeclarationAST *ast)
*symbolTail = new (translationUnit()->memoryPool()) List<Symbol *>(decl); *symbolTail = new (translationUnit()->memoryPool()) List<Symbol *>(decl);
symbolTail = &(*symbolTail)->next; symbolTail = &(*symbolTail)->next;
} }
}
return false; return false;
} }
@@ -3319,6 +3339,14 @@ bool Bind::visit(DeclaratorIdAST *ast)
return false; return false;
} }
bool Bind::visit(DecompositionDeclaratorAST *ast)
{
for (auto it = ast->identifiers->begin(); it != ast->identifiers->end(); ++it)
name(*it);
*_decompositionDeclarator = ast;
return false;
}
bool Bind::visit(NestedDeclaratorAST *ast) bool Bind::visit(NestedDeclaratorAST *ast)
{ {
_type = this->declarator(ast->declarator, _type, _declaratorId); _type = this->declarator(ast->declarator, _type, _declaratorId);

View File

@@ -77,7 +77,9 @@ protected:
const Name *objCSelectorArgument(ObjCSelectorArgumentAST *ast, bool *hasArg); const Name *objCSelectorArgument(ObjCSelectorArgumentAST *ast, bool *hasArg);
void attribute(GnuAttributeAST *ast); void attribute(GnuAttributeAST *ast);
FullySpecifiedType declarator(DeclaratorAST *ast, const FullySpecifiedType &init, DeclaratorIdAST **declaratorId); FullySpecifiedType declarator(DeclaratorAST *ast, const FullySpecifiedType &init,
DeclaratorIdAST **declaratorId,
DecompositionDeclaratorAST **decompDeclarator = nullptr);
void qtInterfaceName(QtInterfaceNameAST *ast); void qtInterfaceName(QtInterfaceNameAST *ast);
void baseSpecifier(BaseSpecifierAST *ast, int colon_token, Class *klass); void baseSpecifier(BaseSpecifierAST *ast, int colon_token, Class *klass);
void ctorInitializer(CtorInitializerAST *ast, Function *fun); void ctorInitializer(CtorInitializerAST *ast, Function *fun);
@@ -275,6 +277,7 @@ protected:
// CoreDeclaratorAST // CoreDeclaratorAST
bool visit(DeclaratorIdAST *ast) override; bool visit(DeclaratorIdAST *ast) override;
bool visit(DecompositionDeclaratorAST *ast) override;
bool visit(NestedDeclaratorAST *ast) override; bool visit(NestedDeclaratorAST *ast) override;
// PostfixDeclaratorAST // PostfixDeclaratorAST
@@ -291,6 +294,7 @@ private:
const Name *_name; const Name *_name;
FullySpecifiedType _type; FullySpecifiedType _type;
DeclaratorIdAST **_declaratorId; DeclaratorIdAST **_declaratorId;
DecompositionDeclaratorAST **_decompositionDeclarator;
int _visibility; int _visibility;
int _objcVisibility; int _objcVisibility;
int _methodKey; int _methodKey;

View File

@@ -1568,6 +1568,8 @@ bool Parser::parseDeclaratorOrAbstractDeclarator(DeclaratorAST *&node, Specifier
return parseAbstractDeclarator(node, decl_specifier_list); return parseAbstractDeclarator(node, decl_specifier_list);
} }
bool Parser::parseCoreDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list, ClassSpecifierAST *) bool Parser::parseCoreDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list, ClassSpecifierAST *)
{ {
DEBUG_THIS_RULE(); DEBUG_THIS_RULE();
@@ -1616,11 +1618,51 @@ bool Parser::parseCoreDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_sp
node = ast; node = ast;
return true; return true;
} }
} else if (const auto decl = parseDecompositionDeclarator()) {
DeclaratorAST *ast = new (_pool) DeclaratorAST;
ast->attribute_list = attributes;
ast->ptr_operator_list = ptr_operators;
ast->core_declarator = decl;
node = ast;
return true;
} }
rewind(start); rewind(start);
return false; return false;
} }
DecompositionDeclaratorAST *Parser::parseDecompositionDeclarator()
{
if (LA() != T_LBRACKET)
return nullptr;
consumeToken();
const auto decl = new (_pool) DecompositionDeclaratorAST;
for (NameListAST **iter = &decl->identifiers; ; iter = &(*iter)->next) {
NameAST *name_ast = nullptr;
if (!parseName(name_ast)) {
error(cursor(), "expected an identifier");
return nullptr;
}
*iter = new (_pool) NameListAST;
(*iter)->value = name_ast;
if (LA() == T_RBRACKET) {
consumeToken();
return decl;
}
if (LA() == T_COMMA) {
consumeToken();
continue;
}
error(cursor(), "expected ',' or ']'");
return nullptr;
}
return nullptr;
}
static bool maybeCppInitializer(DeclaratorAST *declarator) static bool maybeCppInitializer(DeclaratorAST *declarator)
{ {
if (declarator->ptr_operator_list) if (declarator->ptr_operator_list)

View File

@@ -79,6 +79,7 @@ public:
bool parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *declaringClass = nullptr); bool parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *declaringClass = nullptr);
bool parseDeclarationStatement(StatementAST *&node); bool parseDeclarationStatement(StatementAST *&node);
bool parseCoreDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list, ClassSpecifierAST *declaringClass); bool parseCoreDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list, ClassSpecifierAST *declaringClass);
DecompositionDeclaratorAST *parseDecompositionDeclarator();
bool parseDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list, ClassSpecifierAST *declaringClass = nullptr); bool parseDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list, ClassSpecifierAST *declaringClass = nullptr);
bool parseDeleteExpression(ExpressionAST *&node); bool parseDeleteExpression(ExpressionAST *&node);
bool parseDoStatement(StatementAST *&node); bool parseDoStatement(StatementAST *&node);

View File

@@ -2494,6 +2494,13 @@ bool FindUsages::visit(DeclaratorIdAST *ast)
return false; return false;
} }
bool FindUsages::visit(DecompositionDeclaratorAST *ast)
{
for (auto it = ast->identifiers->begin(); it != ast->identifiers->end(); ++it)
name(*it);
return false;
}
bool FindUsages::visit(NestedDeclaratorAST *ast) bool FindUsages::visit(NestedDeclaratorAST *ast)
{ {
// unsigned lparen_token = ast->lparen_token; // unsigned lparen_token = ast->lparen_token;

View File

@@ -282,6 +282,7 @@ protected:
// CoreDeclaratorAST // CoreDeclaratorAST
virtual bool visit(DeclaratorIdAST *ast); virtual bool visit(DeclaratorIdAST *ast);
virtual bool visit(DecompositionDeclaratorAST *ast);
virtual bool visit(NestedDeclaratorAST *ast); virtual bool visit(NestedDeclaratorAST *ast);
// PostfixDeclaratorAST // PostfixDeclaratorAST

View File

@@ -83,6 +83,7 @@ private Q_SLOTS:
void shadowedNames_1(); void shadowedNames_1();
void shadowedNames_2(); void shadowedNames_2();
void staticVariables(); void staticVariables();
void structuredBinding();
void functionNameFoundInArguments(); void functionNameFoundInArguments();
void memberFunctionFalsePositives_QTCREATORBUG2176(); void memberFunctionFalsePositives_QTCREATORBUG2176();
@@ -374,6 +375,42 @@ void tst_FindUsages::staticVariables()
QCOMPARE(findUsages.usages().at(4).type, Usage::Type::Write); QCOMPARE(findUsages.usages().at(4).type, Usage::Type::Write);
} }
void tst_FindUsages::structuredBinding()
{
const QByteArray src = "\n"
"int array[] = {1, 2};\n"
"const auto &[e1, e2] = array;\n"
"const int i1 = e1;\n"
"const int i2 = e2;\n"
;
Document::Ptr doc = Document::create("structuredBinding");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 5);
Snapshot snapshot;
snapshot.insert(doc);
Declaration * const d1 = doc->globalSymbolAt(1)->asDeclaration();
QVERIFY(d1);
Declaration * const d2 = doc->globalSymbolAt(2)->asDeclaration();
QVERIFY(d2);
FindUsages findUsages(src, doc, snapshot, true);
findUsages(d1);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Initialization);
QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Read);
findUsages(d2);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Initialization);
QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Read);
}
void tst_FindUsages::functionNameFoundInArguments() void tst_FindUsages::functionNameFoundInArguments()
{ {
const QByteArray src = const QByteArray src =