Commit 390b4f0e authored by Nikolai Kosjar's avatar Nikolai Kosjar

C++: Fix parsing of "Foo *foo = new Foo()"

It should be parsed as an DeclarationStatement, but instead it was
parsed as an ExpressionStatement.

Regression introduced with

    commit d3c5fff6.
    C++: Fix expensive parsing of expressions

The introduced ASTCache did not save the correct return value of a
parse* function. Because of that, the first return in
Parser::parseExpressionList returned false on the second invocation
(cache hit), instead of true, which resulted in an ExpressionStatement.

Task-number: QTCREATORBUG-13122
Change-Id: I8dbd8852b0909edddcd3195b484f4cea92328cc5
Reviewed-by: default avatarFawzi Mohamed <fawzi.mohamed@digia.com>
parent c2eb91e0
......@@ -172,16 +172,20 @@ public:
ASTCache() {}
void insert(ASTKind astKind, unsigned tokenIndexBeforeParsing,
AST *resultingAST, unsigned resultingTokenIndex)
AST *resultingAST, unsigned resultingTokenIndex, bool resultingReturnValue)
{
const auto key = std::make_pair(astKind, tokenIndexBeforeParsing);
const auto value = std::make_pair(resultingAST, resultingTokenIndex);
const auto keyValue = std::make_pair(key, value);
ParseFunctionResult result;
result.resultingAST = resultingAST;
result.resultingTokenIndex = resultingTokenIndex;
result.returnValue = resultingReturnValue;
const auto keyValue = std::make_pair(key, result);
_cache.insert(keyValue);
}
AST *find(ASTKind astKind, unsigned tokenIndex,
unsigned *resultingTokenIndex, bool *foundInCache) const
unsigned *resultingTokenIndex, bool *foundInCache, bool *returnValue) const
{
const auto key = std::make_pair(astKind, tokenIndex);
const auto it = _cache.find(key);
......@@ -190,8 +194,9 @@ public:
return 0;
} else {
*foundInCache = true;
*resultingTokenIndex = it->second.second;
return it->second.first;
*resultingTokenIndex = it->second.resultingTokenIndex;
*returnValue = it->second.returnValue;
return it->second.resultingAST;
}
}
......@@ -206,9 +211,14 @@ private:
{ return std::hash<int>()(key.first) ^ std::hash<unsigned>()(key.second); }
};
struct ParseFunctionResult {
AST *resultingAST;
unsigned resultingTokenIndex;
bool returnValue;
};
typedef std::pair<int, unsigned> ASTKindAndTokenIndex;
typedef std::pair<AST *, unsigned> ASTAndTokenIndex;
std::unordered_map<ASTKindAndTokenIndex, ASTAndTokenIndex, KeyHasher> _cache;
std::unordered_map<ASTKindAndTokenIndex, ParseFunctionResult, KeyHasher> _cache;
};
#ifndef CPLUSPLUS_NO_DEBUG_RULE
......@@ -227,18 +237,20 @@ inline void debugPrintCheckCache(bool goodCase)
inline void debugPrintCheckCache(bool) {}
#endif
#define CHECK_CACHE(ASTKind, ASTType, returnValueInBadCase) \
#define CHECK_CACHE(ASTKind, ASTType) \
do { \
bool foundInCache; \
unsigned newTokenIndex; \
if (AST *ast = _astCache->find(ASTKind, cursor(), &newTokenIndex, &foundInCache)) { \
unsigned newTokenIndex; \
bool returnValue; \
if (AST *ast = _astCache->find(ASTKind, cursor(), \
&newTokenIndex, &foundInCache, &returnValue)) { \
debugPrintCheckCache(true); \
node = (ASTType *) ast; \
_tokenIndex = newTokenIndex; \
return true; \
return returnValue; \
} else if (foundInCache) { \
debugPrintCheckCache(false); \
return returnValueInBadCase; \
return returnValue; \
} \
} while (0)
......@@ -1931,7 +1943,7 @@ bool Parser::parseTypeParameter(DeclarationAST *&node)
bool Parser::parseTypeId(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
CHECK_CACHE(ASTCache::TypeId, ExpressionAST, false);
CHECK_CACHE(ASTCache::TypeId, ExpressionAST);
SpecifierListAST *type_specifier = 0;
if (parseTypeSpecifier(type_specifier)) {
......@@ -1949,7 +1961,7 @@ bool Parser::parseParameterDeclarationClause(ParameterDeclarationClauseAST *&nod
DEBUG_THIS_RULE();
if (LA() == T_RPAREN)
return true; // nothing to do
CHECK_CACHE(ASTCache::ParameterDeclarationClause, ParameterDeclarationClauseAST, true);
CHECK_CACHE(ASTCache::ParameterDeclarationClause, ParameterDeclarationClauseAST);
const unsigned initialCursor = cursor();
ParameterDeclarationListAST *parameter_declarations = 0;
......@@ -1975,8 +1987,9 @@ bool Parser::parseParameterDeclarationClause(ParameterDeclarationClauseAST *&nod
node = ast;
}
_astCache->insert(ASTCache::ParameterDeclarationClause, initialCursor, node, cursor());
return true;
const bool result = true;
_astCache->insert(ASTCache::ParameterDeclarationClause, initialCursor, node, cursor(), result);
return result;
}
bool Parser::parseParameterDeclarationList(ParameterDeclarationListAST *&node)
......@@ -2911,12 +2924,12 @@ bool Parser::parseTypeIdList(ExpressionListAST *&node)
bool Parser::parseExpressionList(ExpressionListAST *&node)
{
DEBUG_THIS_RULE();
CHECK_CACHE(ASTCache::ExpressionList, ExpressionListAST, false);
CHECK_CACHE(ASTCache::ExpressionList, ExpressionListAST);
unsigned initialCursor = cursor();
if (_languageFeatures.cxx11Enabled) {
bool result = parseInitializerList0x(node);
_astCache->insert(ASTCache::ExpressionList, initialCursor, (AST *) node, cursor());
const bool result = parseInitializerList0x(node);
_astCache->insert(ASTCache::ExpressionList, initialCursor, (AST *) node, cursor(), result);
return result;
}
......@@ -2935,12 +2948,14 @@ bool Parser::parseExpressionList(ExpressionListAST *&node)
expression_list_ptr = &(*expression_list_ptr)->next;
}
}
_astCache->insert(ASTCache::ExpressionList, initialCursor, (AST *) node, cursor());
return true;
const bool result = true;
_astCache->insert(ASTCache::ExpressionList, initialCursor, (AST *) node, cursor(), result);
return result;
}
_astCache->insert(ASTCache::ExpressionList, initialCursor, 0, cursor());
return false;
const bool result = false;
_astCache->insert(ASTCache::ExpressionList, initialCursor, 0, cursor(), result);
return result;
}
bool Parser::parseBaseSpecifier(BaseSpecifierListAST *&node)
......@@ -5427,7 +5442,7 @@ bool Parser::parseCastExpression(ExpressionAST *&node)
}
parse_as_unary_expression:
_astCache->insert(ASTCache::TypeId, initialCursor, 0, cursor());
_astCache->insert(ASTCache::TypeId, initialCursor, 0, cursor(), false);
rewind(lparen_token);
}
......@@ -5528,7 +5543,7 @@ bool Parser::parseConstantExpression(ExpressionAST *&node)
bool Parser::parseExpression(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
CHECK_CACHE(ASTCache::Expression, ExpressionAST, false);
CHECK_CACHE(ASTCache::Expression, ExpressionAST);
unsigned initialCursor = cursor();
if (_expressionDepth > MAX_EXPRESSION_DEPTH)
......@@ -5538,7 +5553,7 @@ bool Parser::parseExpression(ExpressionAST *&node)
bool success = parseCommaExpression(node);
--_expressionDepth;
_astCache->insert(ASTCache::Expression, initialCursor, node, cursor());
_astCache->insert(ASTCache::Expression, initialCursor, node, cursor(), success);
return success;
}
......
......@@ -50,10 +50,12 @@ public:
TranslationUnit *parse(const QByteArray &source,
TranslationUnit::ParseMode mode,
bool blockErrors = false,
bool qtMocRun = false)
bool qtMocRun = false,
bool cxx11Enabled = false)
{
const StringLiteral *fileId = control.stringLiteral("<stdin>");
LanguageFeatures features;
features.cxx11Enabled = cxx11Enabled;
features.objCEnabled = true;
features.qtEnabled = qtMocRun;
features.qtKeywordsEnabled = qtMocRun;
......@@ -79,8 +81,8 @@ public:
TranslationUnit *parseExpression(const QByteArray &source)
{ return parse(source, TranslationUnit::ParseExpression); }
TranslationUnit *parseStatement(const QByteArray &source)
{ return parse(source, TranslationUnit::ParseStatement); }
TranslationUnit *parseStatement(const QByteArray &source, bool cxx11Enabled = false)
{ return parse(source, TranslationUnit::ParseStatement, false, false, cxx11Enabled); }
class Diagnostic: public DiagnosticClient {
public:
......@@ -191,6 +193,8 @@ private slots:
// Qt "keywords"
void q_enum_1();
void declarationWithNewStatement();
void declarationWithNewStatement_data();
void incomplete_ast();
void unnamed_class();
void unnamed_class_data();
......@@ -1748,6 +1752,26 @@ void tst_AST::q_enum_1()
QCOMPARE(unit->spell(e->identifier_token), "e");
}
void tst_AST::declarationWithNewStatement()
{
QFETCH(QByteArray, source);
QSharedPointer<TranslationUnit> unit(parseStatement(source, true));
AST *ast = unit->ast();
QVERIFY(ast);
QVERIFY(ast->asDeclarationStatement());
}
void tst_AST::declarationWithNewStatement_data()
{
QTest::addColumn<QByteArray>("source");
typedef QByteArray _;
QTest::newRow("withoutParentheses") << _("Foo *foo = new Foo;");
QTest::newRow("withParentheses") << _("Foo *foo = new Foo();");
QTest::newRow("withParenthesesAndOneArgument") << _("Foo *foo = new Foo(1);");
}
void tst_AST::incomplete_ast()
{
QSharedPointer<TranslationUnit> unit(parseStatement("class A { virtual void a() =\n"));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment