Commit c56b999f authored by Wang Hoi's avatar Wang Hoi Committed by Nikolai Kosjar

C: Parser: Support parsing of c99 designated initializers

In case:

    int a[6] = { [4] = 29, [2] = 15 };
    struct point { int x, y; };
    struct point p = { .y = 3, .x = 2 };

Grammar change when c99 language feature is enabled:
old grammar:

    braced-init-list :: '{' initializer-list '}'

new grammar:

    braced-init-list :: '{' designated-initializer-list '}'
    designated-initializer-list :: designated-initializer (',' designated-initializer )*
    designated-initializer :: designator* initializer-clause
    designator :: '.' identifier
                | '[' constant-expression ']'

Task-number: QTCREATORBUG-1902
Change-Id: Ib99d6f553f8d0f50ba3eff86f3a2e86d73372426
Reviewed-by: default avatarNikolai Kosjar <nikolai.kosjar@digia.com>
parent d7048518
......@@ -4450,3 +4450,69 @@ unsigned AliasDeclarationAST::lastToken() const
return 1;
}
/** \generated */
unsigned DesignatedInitializerAST::firstToken() const
{
if (designator_list)
if (unsigned candidate = designator_list->firstToken())
return candidate;
if (equal_token)
return equal_token;
if (initializer)
if (unsigned candidate = initializer->firstToken())
return candidate;
return 0;
}
/** \generated */
unsigned DesignatedInitializerAST::lastToken() const
{
if (initializer)
if (unsigned candidate = initializer->lastToken())
return candidate;
if (equal_token)
return equal_token + 1;
if (designator_list)
if (unsigned candidate = designator_list->lastToken())
return candidate;
return 1;
}
unsigned DesignatorAST::firstToken() const
{
if (type == DesignatorAST::Dot) {
if (u.dot.dot_token)
return u.dot.dot_token;
if (u.dot.identifier_token)
return u.dot.identifier_token;
} else if (type == DesignatorAST::Bracket) {
if (u.bracket.lbracket_token)
return u.bracket.lbracket_token;
if (u.bracket.expression)
if (unsigned candidate = u.bracket.expression->firstToken())
return candidate;
if (u.bracket.rbracket_token)
return u.bracket.rbracket_token;
}
return 0;
}
unsigned DesignatorAST::lastToken() const
{
if (type == DesignatorAST::Dot) {
if (u.dot.identifier_token)
return u.dot.identifier_token + 1;
if (u.dot.dot_token)
return u.dot.dot_token + 1;
} else if (type == DesignatorAST::Bracket) {
if (u.bracket.rbracket_token)
return u.bracket.rbracket_token + 1;
if (u.bracket.expression)
if (unsigned candidate = u.bracket.expression->lastToken())
return candidate;
if (u.bracket.lbracket_token)
return u.bracket.lbracket_token + 1;
}
return 1;
}
......@@ -161,6 +161,8 @@ public:
virtual DeclaratorIdAST *asDeclaratorId() { return 0; }
virtual DecltypeSpecifierAST *asDecltypeSpecifier() { return 0; }
virtual DeleteExpressionAST *asDeleteExpression() { return 0; }
virtual DesignatedInitializerAST *asDesignatedInitializer() { return 0; }
virtual DesignatorAST *asDesignator() { return 0; }
virtual DestructorNameAST *asDestructorName() { return 0; }
virtual DoStatementAST *asDoStatement() { return 0; }
virtual DynamicExceptionSpecificationAST *asDynamicExceptionSpecification() { return 0; }
......@@ -4527,6 +4529,72 @@ protected:
virtual bool match0(AST *, ASTMatcher *);
};
class DesignatorAST: public AST
{
public:
enum Type
{
Invalid,
Dot,
Bracket
};
Type type;
union Designator
{
struct DotDesignator
{
unsigned dot_token;
unsigned identifier_token;
} dot;
struct BracketDesignator
{
unsigned lbracket_token;
ExpressionAST *expression;
unsigned rbracket_token;
} bracket;
} u;
public:
DesignatorAST()
: type(Invalid)
{}
virtual DesignatorAST *asDesignator() { return this; }
virtual unsigned firstToken() const;
virtual unsigned lastToken() const;
virtual DesignatorAST *clone(MemoryPool *pool) const;
protected:
virtual void accept0(ASTVisitor *visitor);
virtual bool match0(AST *, ASTMatcher *);
};
class DesignatedInitializerAST: public ExpressionAST
{
public:
DesignatorListAST *designator_list;
unsigned equal_token;
ExpressionAST *initializer;
public:
DesignatedInitializerAST()
: designator_list(0)
, equal_token(0)
, initializer(0)
{}
virtual DesignatedInitializerAST *asDesignatedInitializer() { return this; }
virtual unsigned firstToken() const;
virtual unsigned lastToken() const;
virtual DesignatedInitializerAST *clone(MemoryPool *pool) const;
protected:
virtual void accept0(ASTVisitor *visitor);
virtual bool match0(AST *, ASTMatcher *);
};
} // namespace CPlusPlus
#endif // CPLUSPLUS_AST_H
......@@ -1762,3 +1762,21 @@ BracedInitializerAST *BracedInitializerAST::clone(MemoryPool *pool) const
return ast;
}
DesignatorAST *DesignatorAST::clone(MemoryPool *pool) const
{
DesignatorAST *ast = new (pool) DesignatorAST;
return ast;
}
DesignatedInitializerAST *DesignatedInitializerAST::clone(MemoryPool *pool) const
{
DesignatedInitializerAST *ast = new (pool) DesignatedInitializerAST;
for (DesignatorListAST *iter = designator_list, **ast_iter = &ast->designator_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
*ast_iter = new (pool) DesignatorListAST((iter->value) ? iter->value->clone(pool) : 0);
ast->equal_token = equal_token;
if (initializer)
ast->initializer = initializer->clone(pool);
return ast;
}
......@@ -1192,3 +1192,19 @@ bool BracedInitializerAST::match0(AST *pattern, ASTMatcher *matcher)
return false;
}
bool DesignatorAST::match0(AST *pattern, ASTMatcher *matcher)
{
if (DesignatorAST *_other = pattern->asDesignator())
return matcher->match(this, _other);
return false;
}
bool DesignatedInitializerAST::match0(AST *pattern, ASTMatcher *matcher)
{
if (DesignatedInitializerAST *_other = pattern->asDesignatedInitializer())
return matcher->match(this, _other);
return false;
}
......@@ -2999,3 +2999,31 @@ bool ASTMatcher::match(BracedInitializerAST *node, BracedInitializerAST *pattern
return true;
}
bool ASTMatcher::match(DesignatorAST *node, DesignatorAST *pattern)
{
(void) node;
(void) pattern;
return true;
}
bool ASTMatcher::match(DesignatedInitializerAST *node, DesignatedInitializerAST *pattern)
{
(void) node;
(void) pattern;
if (! pattern->designator_list)
pattern->designator_list = node->designator_list;
else if (! AST::match(node->designator_list, pattern->designator_list, this))
return false;
pattern->equal_token = node->equal_token;
if (! pattern->initializer)
pattern->initializer = node->initializer;
else if (! AST::match(node->initializer, pattern->initializer, this))
return false;
return true;
}
......@@ -66,6 +66,8 @@ public:
virtual bool match(DeclaratorIdAST *node, DeclaratorIdAST *pattern);
virtual bool match(DecltypeSpecifierAST *node, DecltypeSpecifierAST *pattern);
virtual bool match(DeleteExpressionAST *node, DeleteExpressionAST *pattern);
virtual bool match(DesignatedInitializerAST *node, DesignatedInitializerAST *pattern);
virtual bool match(DesignatorAST *node, DesignatorAST *pattern);
virtual bool match(DestructorNameAST *node, DestructorNameAST *pattern);
virtual bool match(DoStatementAST *node, DoStatementAST *pattern);
virtual bool match(DynamicExceptionSpecificationAST *node, DynamicExceptionSpecificationAST *pattern);
......
......@@ -1147,6 +1147,20 @@ public:
return __ast;
}
DesignatorAST *Designator()
{
DesignatorAST *__ast = new (&pool) DesignatorAST;
return __ast;
}
DesignatedInitializerAST *DesignatedInitializer(DesignatorListAST *designator_list = 0, ExpressionAST *initializer = 0)
{
DesignatedInitializerAST *__ast = new (&pool) DesignatedInitializerAST;
__ast->designator_list = designator_list;
__ast->initializer = initializer;
return __ast;
}
AttributeListAST *AttributeList(AttributeAST *value, AttributeListAST *next = 0)
{
AttributeListAST *__list = new (&pool) AttributeListAST;
......@@ -1195,6 +1209,14 @@ public:
return __list;
}
DesignatorListAST *DesignatorList(DesignatorAST *value, DesignatorListAST *next = 0)
{
DesignatorListAST *__list = new (&pool) DesignatorListAST;
__list->next = next;
__list->value = value;
return __list;
}
EnumeratorListAST *EnumeratorList(EnumeratorAST *value, EnumeratorListAST *next = 0)
{
EnumeratorListAST *__list = new (&pool) EnumeratorListAST;
......
......@@ -1279,3 +1279,19 @@ void BracedInitializerAST::accept0(ASTVisitor *visitor)
visitor->endVisit(this);
}
void DesignatorAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
}
visitor->endVisit(this);
}
void DesignatedInitializerAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
accept(designator_list, visitor);
accept(initializer, visitor);
}
visitor->endVisit(this);
}
......@@ -108,6 +108,8 @@ public:
virtual bool visit(DeclaratorIdAST *) { return true; }
virtual bool visit(DecltypeSpecifierAST *) { return true; }
virtual bool visit(DeleteExpressionAST *) { return true; }
virtual bool visit(DesignatedInitializerAST *) { return true; }
virtual bool visit(DesignatorAST *) { return true; }
virtual bool visit(DestructorNameAST *) { return true; }
virtual bool visit(DoStatementAST *) { return true; }
virtual bool visit(DynamicExceptionSpecificationAST *) { return true; }
......@@ -254,6 +256,8 @@ public:
virtual void endVisit(DeclaratorIdAST *) {}
virtual void endVisit(DecltypeSpecifierAST *) {}
virtual void endVisit(DeleteExpressionAST *) {}
virtual void endVisit(DesignatedInitializerAST *) {}
virtual void endVisit(DesignatorAST *) {}
virtual void endVisit(DestructorNameAST *) {}
virtual void endVisit(DoStatementAST *) {}
virtual void endVisit(DynamicExceptionSpecificationAST *) {}
......
......@@ -68,6 +68,8 @@ class DeclaratorAST;
class DeclaratorIdAST;
class DecltypeSpecifierAST;
class DeleteExpressionAST;
class DesignatedInitializerAST;
class DesignatorAST;
class DestructorNameAST;
class DoStatementAST;
class DynamicExceptionSpecificationAST;
......@@ -215,6 +217,8 @@ typedef List<ObjCPropertyAttributeAST *> ObjCPropertyAttributeListAST;
typedef List<ObjCMessageArgumentDeclarationAST *> ObjCMessageArgumentDeclarationListAST;
typedef List<ObjCSynthesizedPropertyAST *> ObjCSynthesizedPropertyListAST;
typedef List<DesignatorAST *> DesignatorListAST;
} // namespace CPlusPlus
......
......@@ -2664,7 +2664,7 @@ bool Parser::parseInitializerList0x(ExpressionListAST *&node)
ExpressionListAST **expression_list_ptr = &node;
ExpressionAST *expression = 0;
if (parseInitializerClause0x(expression)) {
if (parseDesignatedInitializer(expression)) {
*expression_list_ptr = new (_pool) ExpressionListAST;
(*expression_list_ptr)->value = expression;
expression_list_ptr = &(*expression_list_ptr)->next;
......@@ -2675,7 +2675,7 @@ bool Parser::parseInitializerList0x(ExpressionListAST *&node)
while (LA() == T_COMMA && LA(2) != T_RBRACE) {
consumeToken(); // consume T_COMMA
if (parseInitializerClause0x(expression)) {
if (parseDesignatedInitializer(expression)) {
*expression_list_ptr = new (_pool) ExpressionListAST;
(*expression_list_ptr)->value = expression;
......@@ -5484,6 +5484,50 @@ bool Parser::lookAtObjCSelector() const
return false;
}
// designated-initializer ::= designator* T_EQUAL initializer-clause
//
bool Parser::parseDesignatedInitializer(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
if (!_languageFeatures.c99Enabled || (LA() != T_DOT && LA() != T_LBRACKET))
return parseInitializerClause0x(node);
DesignatedInitializerAST *ast = new (_pool) DesignatedInitializerAST;
DesignatorListAST **designator_list_ptr = &ast->designator_list;
DesignatorAST *designator = 0;
while (parseDesignator(designator)) {
*designator_list_ptr = new (_pool) DesignatorListAST;
(*designator_list_ptr)->value = designator;
designator_list_ptr = &(*designator_list_ptr)->next;
}
match(T_EQUAL, &ast->equal_token);
parseInitializerClause0x(ast->initializer);
node = ast;
return true;
}
// designator ::= T_DOT T_IDENTIFIER
// T_LBRACKET constant-expression T_BRACKET
//
bool Parser::parseDesignator(DesignatorAST *&node)
{
DesignatorAST *ast = new (_pool) DesignatorAST;
if (LA() == T_DOT) {
ast->type = DesignatorAST::Dot;
ast->u.dot.dot_token = consumeToken();
match(T_IDENTIFIER, &ast->u.dot.identifier_token);
} else if (LA() == T_LBRACKET) {
ast->type = DesignatorAST::Bracket;
ast->u.bracket.lbracket_token = consumeToken();
parseConstantExpression(ast->u.bracket.expression);
match(T_RBRACKET, &ast->u.bracket.rbracket_token);
} else {
return false;
}
node = ast;
return true;
}
// objc-class-declaraton ::= T_AT_CLASS (T_IDENTIFIER @ T_COMMA) T_SEMICOLON
//
bool Parser::parseObjCClassForwardDeclaration(DeclarationAST *&node)
......
......@@ -246,6 +246,11 @@ public:
bool lookAtObjCSelector() const;
// c99
bool parseDesignatedInitializerList(ExpressionListAST *&node);
bool parseDesignatedInitializer(ExpressionAST *&node);
bool parseDesignator(DesignatorAST *&node);
bool skipUntil(int token);
void skipUntilDeclaration();
bool skipUntilStatement();
......
......@@ -390,6 +390,7 @@ struct LanguageFeatures
unsigned int qtKeywordsEnabled : 1; // If Qt is used but QT_NO_KEYWORDS defined
unsigned int cxx11Enabled : 1;
unsigned int objCEnabled : 1;
unsigned int c99Enabled : 1;
};
};
};
......
......@@ -288,6 +288,7 @@ Document::Document(const QString &fileName)
features.qtKeywordsEnabled = true;
features.cxx11Enabled = true;
features.objCEnabled = true;
features.c99Enabled = true;
_translationUnit = new TranslationUnit(_control, fileId);
_translationUnit->setLanguageFeatures(features);
(void) _control->switchTranslationUnit(_translationUnit);
......
......@@ -106,6 +106,7 @@ bool CppAutoCompleter::isInCommentHelper(const QTextCursor &cursor, Token *retTo
features.qtKeywordsEnabled = false;
features.qtMocRunEnabled = false;
features.cxx11Enabled = true;
features.c99Enabled = true;
SimpleLexer tokenize;
tokenize.setLanguageFeatures(features);
......
......@@ -77,6 +77,7 @@ void CppHighlighter::highlightBlock(const QString &text)
// FIXME: Check defaults or get from document.
LanguageFeatures features;
features.cxx11Enabled = true;
features.c99Enabled = true;
SimpleLexer tokenize;
tokenize.setLanguageFeatures(features);
......
......@@ -495,6 +495,13 @@ bool CheckSymbols::visit(EnumeratorAST *ast)
return true;
}
bool CheckSymbols::visit(DesignatorAST *ast)
{
if (ast->type == DesignatorAST::Dot)
addUse(ast->u.dot.identifier_token, CppHighlightingSupport::FieldUse);
return true;
}
bool CheckSymbols::visit(SimpleDeclarationAST *ast)
{
NameAST *declrIdNameAST = 0;
......
......@@ -160,6 +160,8 @@ protected:
virtual bool visit(CPlusPlus::MemInitializerAST *ast);
virtual bool visit(CPlusPlus::EnumeratorAST *ast);
virtual bool visit(CPlusPlus::DesignatorAST *ast);
CPlusPlus::NameAST *declaratorId(CPlusPlus::DeclaratorAST *ast) const;
static unsigned referenceToken(CPlusPlus::NameAST *name);
......
......@@ -83,6 +83,7 @@ QFuture<TextEditor::HighlightingResult> CppHighlightingSupportInternal::highligh
// FIXME: Check default values or get from document.
LanguageFeatures features;
features.cxx11Enabled = true;
features.c99Enabled = true;
SimpleLexer tokenize;
tokenize.setLanguageFeatures(features);
......
include(../shared/shared.pri)
# Inject the source dir for referencing test data from shadow builds.
DEFINES += SRCDIR=\\\"$$PWD\\\"
SOURCES += tst_c99.cpp
OTHER_FILES += \
data/designatedInitializer.1.c
import qbs
import "../cplusplusautotest.qbs" as CPlusPlusAutotest
CPlusPlusAutotest {
name: "C99 autotest"
Group {
name: "Source Files"
files: "tst_c99.cpp"
}
Group {
name: "Data Files"
prefix: "data/"
fileTags: ["data"]
files: [
"designatedInitializer.1.c",
]
}
cpp.defines: base.concat(['SRCDIR="' + path + '"'])
}
int aa[4] = { [2] = 3, [1] = 6 };
static short grid[3] [4] = { [0][0]=8, [0][1]=6,
[0][2]=4, [0][3]=1,
[2][0]=9, [2][1]=3,
[2][2]=1, [2][3]=1 };
int a[10] = {2, 4, [8]=9, 10};
int a[MAX] = {
1, 3, 5, 7, 9, [MAX-5] = 8, 6, 4, 2, 0
};
struct xyz {
int a;
int b;
int c;
} klm = { .a = 99, .c = 100 };
struct a {
struct b {
int c;
int d;
} e;
float f;
} g = {.e.c = 3 };
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include <cplusplus/CPlusPlus.h>
#include <cplusplus/CppDocument.h>
#include <cplusplus/LookupContext.h>
#include <QtTest>
#include <QObject>
#include <QFile>
//TESTED_COMPONENT=src/libs/cplusplus
using namespace CPlusPlus;
#define VERIFY_ERRORS() \
do { \
QByteArray expectedErrors; \
if (!errorFile.isEmpty()) { \
QFile e(testdata(errorFile)); \
if (e.open(QFile::ReadOnly)) \
expectedErrors = QTextStream(&e).readAll().toUtf8(); \
} \
QCOMPARE(QString::fromLatin1(errors), QString::fromLatin1(expectedErrors)); \
} while (0)
class tst_c99: public QObject
{
Q_OBJECT
/*
Returns the path to some testdata file or directory.
*/
static QString testdata(const QString &name = QString())
{
static const QString dataDirectory = QLatin1String(SRCDIR "/data");
QString result = dataDirectory;
if (!name.isEmpty()) {
result += QLatin1Char('/');
result += name;
}
return result;
}
struct Client: CPlusPlus::DiagnosticClient {
QByteArray *errors;
Client(QByteArray *errors)
: errors(errors)
{
}
virtual void report(int level,
const StringLiteral *fileName,
unsigned line, unsigned column,
const char *format, va_list ap)
{
if (! errors)
return;
static const char *const pretty[] = { "warning", "error", "fatal" };
QString str;
str.sprintf("%s:%d:%d: %s: ", fileName->chars(), line, column, pretty[level]);
errors->append(str.toUtf8());
str.vsprintf(format, ap);
errors->append(str.toUtf8());
errors->append('\n');
}
};
Document::Ptr document(const QString &fileName, QByteArray *errors = 0)
{
Document::Ptr doc = Document::create(fileName);
QFile file(testdata(fileName));
if (file.open(QFile::ReadOnly)) {
LanguageFeatures features;
features.c99Enabled = true;
Client client(errors);
doc->control()->setDiagnosticClient(&client);
doc->setUtf8Source(QTextStream(&file).readAll().toUtf8());
doc->translationUnit()->setLanguageFeatures(features);
doc->check();
doc->control()->setDiagnosticClient(0);
} else {
qWarning() << "could not read file" << fileName;
}
return doc;
}
private Q_SLOTS:
//
// checks for the syntax
//
void parse_data();
void parse();
};
void tst_c99::parse_data()
{
QTest::addColumn<QString>("file");
QTest::addColumn<QString>("errorFile");
QTest::newRow("designatedInitializer.1") << "designatedInitializer.1.c" << "";
QTest::newRow("designatedInitializer.2") << "designatedInitializer.2.c" << "";
}
void tst_c99::parse()
{
QFETCH(QString, file);
QFETCH(QString, errorFile);
QByteArray errors;
Document::Ptr doc = document(file, &errors);
if (! qgetenv("DEBUG").isNull())
printf("%s\n", errors.constData());
VERIFY_ERRORS();
}