From 6a06b5b3d644b8ac54c90d95febc952e9b541992 Mon Sep 17 00:00:00 2001 From: Roy Qu Date: Mon, 7 Aug 2023 14:23:57 +0800 Subject: [PATCH] - fix: Dummy struct/enum symbols shouldn't be shown in the completion suggestion. - enhancement: Support optional enum name. - enhancement: Support optional enum type. - enhancement: Support simple const expression evaluation for enum values. - fix: Accessibilty for inherited members are not correct calculated in multiple inheritance. - fix: Can't handle full class name when handle inheritance. --- NEWS.md | 8 +- RedPandaIDE/editor.cpp | 22 - RedPandaIDE/parser/cppparser.cpp | 426 +++++++++++++++----- RedPandaIDE/parser/cppparser.h | 6 + RedPandaIDE/parser/parserutils.h | 3 +- RedPandaIDE/widgets/classbrowser.cpp | 17 +- RedPandaIDE/widgets/codecompletionpopup.cpp | 4 +- 7 files changed, 359 insertions(+), 127 deletions(-) diff --git a/NEWS.md b/NEWS.md index e1f06391..ae14f213 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,12 @@ Red Panda C++ Version 2.24 - enhancement: Press left/right arrow will move caret to the begin/end of the selection. - enhancement: Press up/down arrow will move caret up/down from the begin/end of the selection. - enhancement: Show progress dialog if the time for searching compilers is too long. + - fix: Dummy struct/enum symbols shouldn't be shown in the completion suggestion. + - enhancement: Support optional enum name. + - enhancement: Support optional enum type. + - enhancement: Support simple const expression evaluation for enum values. + - fix: Accessibilty for inherited members are not correct calculated in multiple inheritance. + - fix: Can't handle full class name when handle inheritance. Red Panda C++ Version 2.23 @@ -1054,7 +1060,7 @@ Red Panda C++ Version 0.14.0 - enhancement: show custom icon set folder in options -> enviroment -> folders - enhancement: new class ( to project) wizard - enhancement: greatly speed up code completion - - fix: code folding calcuation not correct when some codes are folded and editing after them + - fix: code folding calculation not correct when some codes are folded and editing after them - enhancement: code completion ui redesigned - fix: mainwindow action's short cut doesn't work, if the action is not in menu or toolbar - fix: when run all cases for a problem, processing of output is slow diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index e9c3191a..e3a56714 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -4018,28 +4018,6 @@ QString Editor::getParserHint(const QStringList& expression,const QString &/*s*/ if (statement->kind == StatementKind::skFunction || statement->kind == StatementKind::skConstructor || statement->kind == StatementKind::skDestructor) { - //PStatement parentScope = statement->parentScope.lock(); -// if (parentScope && parentScope->kind == StatementKind::skNamespace) { -// PStatementList namespaceStatementsList = -// mParser->findNamespace(parentScope->command); -// if (namespaceStatementsList) { -// int counts=0; -// foreach (const PStatement& namespaceStatement, *namespaceStatementsList) { -// QString hint = getHintForFunction(statement,namespaceStatement, -// mFilename,line); -// if (!hint.isEmpty()) { -// counts++; -// if (!result.isEmpty()) -// result += "\n"; -// if (counts>4) { -// result += "..."; -// break; -// } -// result += hint; -// } -// } -// } -// } else result = getHintForFunction(statement,mFilename,line); } else if (statement->line>0) { QFileInfo fileInfo(statement->fileName); diff --git a/RedPandaIDE/parser/cppparser.cpp b/RedPandaIDE/parser/cppparser.cpp index 91b474cc..cb2ddd40 100644 --- a/RedPandaIDE/parser/cppparser.cpp +++ b/RedPandaIDE/parser/cppparser.cpp @@ -266,7 +266,9 @@ PStatement CppParser::doFindStatement(const QString &fullname) const return PStatement(); PStatement parentStatement; PStatement statement; - foreach (const QString& phrase, phrases) { + + for (int i=(phrases[0].isEmpty()?1:0);ikind == StatementKind::skNamespace) { PStatementList lst = doFindNamespace(parentStatement->fullName); foreach (const PStatement& namespaceStatement, *lst) { @@ -1143,7 +1145,13 @@ QString CppParser::prettyPrintStatement(const PStatement& statement, const QStri result = "enum "+statement->command; break; case StatementKind::skEnum: - result = statement->type + "::" + statement->command; + if (!statement->type.isEmpty()) + result = statement->type + "::"; + else + result = ""; + result += statement->command; + if (!statement->value.isEmpty()) + result += "(" + statement->value + ")"; break; case StatementKind::skTypedef: result = "typedef "+statement->type+" "+statement->command; @@ -1518,25 +1526,53 @@ void CppParser::setInheritance(int index, const PStatement& classStatement, bool //skip to matching ')' index=mTokenizer[index]->matchIndex; } else if (inheritScopeType == StatementAccessibility::None) { - if (currentText !=',' - && currentText!=':') { + if (currentText=="::" + || isIdentChar(currentText[0])) { + + QString basename = currentText; + bool isGlobal = false; + index++; + if (basename=="::") { + if (index>=tokenCount || !isIdentChar(mTokenizer[index]->text[0])) { + return; + } + isGlobal=true; + basename=mTokenizer[index]->text; + index++; + } + //remove template staff if (basename.endsWith('>')) { int pBegin = basename.indexOf('<'); if (pBegin>=0) basename.truncate(pBegin); } + + while (index+1text=="::" + && isIdentChar(mTokenizer[index+1]->text[0])){ + basename += "::" + mTokenizer[index+1]->text; + index+=2; + //remove template staff + if (basename.endsWith('>')) { + int pBegin = basename.indexOf('<'); + if (pBegin>=0) + basename.truncate(pBegin); + } + } + // Find the corresponding PStatement PStatement statement = doFindStatementOf(mCurrentFile,basename, - classStatement->parentScope.lock()); + isGlobal?PStatement():classStatement->parentScope.lock()); if (statement && statement->kind == StatementKind::skClass) { inheritClassStatement(classStatement,isStruct,statement,lastInheritScopeType); } } + } else { + lastInheritScopeType = inheritScopeType; } index++; - lastInheritScopeType = inheritScopeType; if (index >= tokenCount) break; if (mTokenizer[index]->text.front() == '{' @@ -1717,6 +1753,174 @@ QStringList CppParser::sortFilesByIncludeRelations(const QSet &files) return result; } +int CppParser::evaluateConstExpr(int endIndex, bool &ok) +{ + int result = 0; + if (mIndex>=endIndex) { + ok=false; + return 0; + } + result = evaluateAdditionConstExpr(endIndex,ok); + if (mIndex!=endIndex) + ok = false; + return result; +} + +int CppParser::evaluateAdditionConstExpr(int endIndex, bool &ok) +{ + int result = 0; + if (mIndex>=endIndex) { + ok=false; + return 0; + } + result = evaluateMultiplyConstExpr(endIndex,ok); + if (!ok) + return result; + while (mIndextext=='+') { + mIndex++; + int temp = evaluateMultiplyConstExpr(endIndex,ok); + if (!ok) + return result; + result+=temp; + } else if (mTokenizer[mIndex]->text=='-') { + mIndex++; + int temp = evaluateMultiplyConstExpr(endIndex,ok); + if (!ok) + return result; + result-=temp; + } else + break; + } + return result; +} + +int CppParser::evaluateMultiplyConstExpr(int endIndex, bool &ok) +{ + int result = 0; + if (mIndex>=endIndex) { + ok=false; + return 0; + } + result = evaluateConstExprTerm(endIndex,ok); + if (!ok) + return result; + while (mIndextext=='*') { + mIndex++; + int temp = evaluateConstExprTerm(endIndex,ok); + if (!ok) + return result; + result*=temp; + } else if (mTokenizer[mIndex]->text=='/') { + mIndex++; + int temp = evaluateConstExprTerm(endIndex,ok); + if (!ok) + return result; + result/=temp; + } else if (mTokenizer[mIndex]->text=='%') { + mIndex++; + int temp = evaluateConstExprTerm(endIndex,ok); + if (!ok) + return result; + result%=temp; + } else + break; + } + return result; +} + +int CppParser::evaluateConstExprTerm(int endIndex, bool &ok) +{ + int result = 0; + if (mIndex>=endIndex) { + ok=false; + return 0; + } + if (mTokenizer[mIndex]->text=="(") { + mIndex++; + result = evaluateConstExpr(endIndex, ok); + if (mIndex>=endIndex || mTokenizer[mIndex]->text!=')') + ok=false; + mIndex++; + } else if (isIdentChar(mTokenizer[mIndex]->text[0]) + || mTokenizer[mIndex]->text=="::") { + QString s = mTokenizer[mIndex]->text; + QSet searched; + mIndex++; + if (s=="::") { + if (mIndex>=endIndex || !isIdentChar(mTokenizer[mIndex]->text[0])) { + ok=false; + return result; + } + s+=mTokenizer[mIndex]->text; + mIndex++; + } + while (mIndex+1text=="::" + && isIdentChar(mTokenizer[mIndex+1]->text[0])){ + s += "::" + mTokenizer[mIndex+1]->text; + mIndex+=2; + } + while (true){ + //prevent infinite loop + if (searched.contains(s)) { + ok=false; + return result; + } + searched.insert(s); + PStatement statement = doFindStatement(s); + if (!statement) { + ok=false; + return result; + } + if (statement->kind == StatementKind::skEnum) { + result = statement->value.toInt(&ok); + break; + } else if (statement->kind == StatementKind::skPreprocessor) { + if (!statement->args.isEmpty()) { + ok=false; + return result; + } + QString macroText = statement->value; + if (macroText.isEmpty()) { + ok=false; + return result; + } + if (isDigitChar(macroText[0])) { + result = evaluateLiteralNumber(endIndex,ok); + } else { + s = macroText; + } + } + } + } else { + result = evaluateLiteralNumber(endIndex,ok); + mIndex++; + } + return result; +} + +int CppParser::evaluateLiteralNumber(int endIndex, bool &ok) +{ + int result = 0; + if (mIndex>=endIndex) { + ok=false; + return 0; + } + if (mTokenizer[mIndex]->text.startsWith("0x") + || mTokenizer[mIndex]->text.startsWith("0X")) + result = mTokenizer[mIndex]->text.mid(2).toInt(&ok,16); + else if (mTokenizer[mIndex]->text.startsWith("0b") + || mTokenizer[mIndex]->text.startsWith("0B")) + result = mTokenizer[mIndex]->text.mid(2).toInt(&ok,2); + else if (mTokenizer[mIndex]->text.startsWith("0")) + result = mTokenizer[mIndex]->text.toInt(&ok,8); + else + result = mTokenizer[mIndex]->text.toInt(&ok); + return result; +} + bool CppParser::checkForKeyword(KeywordType& keywordType) { keywordType = mCppKeywords.value(mTokenizer[mIndex]->text,KeywordType::NotKeyword); @@ -2349,9 +2553,9 @@ void CppParser::handleEnum(bool isTypedef) //enum class isEnumClass = true; mIndex++; //skip class - } bool isAdhocVar=false; + bool isNonameEnum=false; int endIndex=-1; if ((mIndex< tokenCount) && mTokenizer[mIndex]->text.startsWith('{')) { // enum {...} NAME // Skip to the closing brace @@ -2360,9 +2564,12 @@ void CppParser::handleEnum(bool isTypedef) if (i + 1 < tokenCount) { enumName = mTokenizer[i + 1]->text.trimmed(); if (!isIdentifierOrPointer(enumName)) { - //not a valid enum, skip to j - mIndex=indexOfNextSemicolon(i+1)+1; - return; + if (isTypedef || isEnumClass) { + //not a valid enum, skip to j + mIndex=indexOfNextSemicolon(i+1)+1; + return; + } else + isNonameEnum = true; } if (!isTypedef) { //it's an ad-hoc enum var define; @@ -2371,13 +2578,20 @@ void CppParser::handleEnum(bool isTypedef) mIndex=indexOfNextSemicolon(i+1)+1; return; } - enumName = "__enum__"+enumName+"__"; + enumName = "__@enum@__"+enumName+"__"; isAdhocVar=true; } } endIndex=i+1; } else if (mIndex+1< tokenCount && mTokenizer[mIndex+1]->text.startsWith('{')){ // enum NAME {...}; enumName = mTokenizer[mIndex]->text; + mIndex++; + } else if (mIndex+1< tokenCount && mTokenizer[mIndex+1]->text.startsWith(':')){ // enum NAME:int {...}; + enumName = mTokenizer[mIndex]->text; + //skip : + mIndex = indexOfNextLeftBrace(mIndex); + if (mIndex>tokenCount) + return; } else { // enum NAME blahblah // it's an old c-style enum variable definition @@ -2386,34 +2600,37 @@ void CppParser::handleEnum(bool isTypedef) // Add statement for enum name too PStatement enumStatement; - if (isEnumClass) { - enumStatement=addStatement( - getCurrentScope(), - mCurrentFile, - "enum class", - enumName, - "", - "", - "", - startLine, - StatementKind::skEnumClassType, - getScope(), - mCurrentMemberAccessibility, - StatementProperty::spHasDefinition); - } else { - enumStatement=addStatement( - getCurrentScope(), - mCurrentFile, - "enum", - enumName, - "", - "", - "", - startLine, - StatementKind::skEnumType, - getScope(), - mCurrentMemberAccessibility, - StatementProperty::spHasDefinition); + if (!isNonameEnum) { + if (isEnumClass) { + enumStatement=addStatement( + getCurrentScope(), + mCurrentFile, + "enum class", + enumName, + "", + "", + "", + startLine, + StatementKind::skEnumClassType, + getScope(), + mCurrentMemberAccessibility, + StatementProperty::spHasDefinition); + } else { + enumStatement=addStatement( + getCurrentScope(), + mCurrentFile, + "enum", + enumName, + "", + "", + "", + startLine, + StatementKind::skEnumType, + getScope(), + mCurrentMemberAccessibility, + isAdhocVar?(StatementProperty::spHasDefinition|StatementProperty::spDummyStatement) + :StatementProperty::spHasDefinition ); + } } if (isAdhocVar) { //Ad-hoc var definition @@ -2454,69 +2671,81 @@ void CppParser::handleEnum(bool isTypedef) mIndex++; // Call every member "enum NAME ITEMNAME" - QString lastType("enum"); - if (!enumName.isEmpty()) - lastType += ' ' + enumName; + QString lastType; + if (enumStatement && !isAdhocVar) + lastType = "enum " + enumName; QString cmd; QString args; - if (mTokenizer[mIndex]->text!='}') { - while ((mIndex < tokenCount) && - mTokenizer[mIndex]->text!='}') { - if (mTokenizer[mIndex]->text=="=") { - mIndex=indexOfNextPeriodOrSemicolon(mIndex); - continue; - } else if (tokenIsIdentifier(mTokenizer[mIndex]->text)) { - cmd = mTokenizer[mIndex]->text; - args = ""; - if (isEnumClass) { - if (enumStatement) { - addStatement( - enumStatement, - mCurrentFile, - lastType, - cmd, - args, - "", - "", - mTokenizer[mIndex]->line, - StatementKind::skEnum, - getScope(), - mCurrentMemberAccessibility, - StatementProperty::spHasDefinition); + int value=0; + bool canCalcValue=true; + while ((mIndex < tokenCount) && + mTokenizer[mIndex]->text!='}') { + if (tokenIsIdentifier(mTokenizer[mIndex]->text)) { + cmd = mTokenizer[mIndex]->text; + args = ""; + if (mIndex+1text=="=") { + mIndex+=2; + if (mIndexline, - StatementKind::skEnum, - getScope(), - mCurrentMemberAccessibility, - StatementProperty::spHasDefinition); - } - addStatement( - getCurrentScope(), - mCurrentFile, - lastType, - cmd, - "", - "", - "", - mTokenizer[mIndex]->line, - StatementKind::skEnum, - getScope(), - mCurrentMemberAccessibility, - StatementProperty::spHasDefinition); + mIndex = endIndex - 1; } } - mIndex ++ ; + if (isEnumClass) { + if (enumStatement) { + addStatement( + enumStatement, + mCurrentFile, + lastType, + cmd, + args, + "", + canCalcValue?QString("%1").arg(value):"", + mTokenizer[mIndex]->line, + StatementKind::skEnum, + getScope(), + mCurrentMemberAccessibility, + StatementProperty::spHasDefinition); + } + } else { + QString strValue=canCalcValue?QString("%1").arg(value):""; + if (enumStatement) { + addStatement( + enumStatement, + mCurrentFile, + lastType, + cmd, + args, + "", + strValue, + mTokenizer[mIndex]->line, + StatementKind::skEnum, + getScope(), + mCurrentMemberAccessibility, + StatementProperty::spHasDefinition); + } + addStatement( + getCurrentScope(), + mCurrentFile, + lastType, + cmd, + "", + "", + strValue, + mTokenizer[mIndex]->line, + StatementKind::skEnum, + getScope(), + mCurrentMemberAccessibility, + StatementProperty::spHasDefinition); + } + value++; } + mIndex ++ ; } if (mIndex &files); + int evaluateConstExpr(int endIndex, bool &ok); + int evaluateAdditionConstExpr(int endIndex, bool &ok); + int evaluateMultiplyConstExpr(int endIndex, bool &ok); + int evaluateConstExprTerm(int endIndex, bool &ok); + int evaluateLiteralNumber(int endIndex, bool &ok); + bool checkForKeyword(KeywordType &keywordType); bool checkForNamespace(KeywordType keywordType); bool checkForPreprocessor(); diff --git a/RedPandaIDE/parser/parserutils.h b/RedPandaIDE/parser/parserutils.h index 543be90b..dce49cf1 100644 --- a/RedPandaIDE/parser/parserutils.h +++ b/RedPandaIDE/parser/parserutils.h @@ -153,7 +153,8 @@ enum StatementProperty { spOverride = 0x0040, spConstexpr = 0x0080, spFunctionPointer = 0x0100, - spOperatorOverloading = 0x0200 + spOperatorOverloading = 0x0200, + spDummyStatement = 0x0400 }; Q_DECLARE_FLAGS(StatementProperties, StatementProperty) diff --git a/RedPandaIDE/widgets/classbrowser.cpp b/RedPandaIDE/widgets/classbrowser.cpp index e069021d..77a23633 100644 --- a/RedPandaIDE/widgets/classbrowser.cpp +++ b/RedPandaIDE/widgets/classbrowser.cpp @@ -148,12 +148,18 @@ QVariant ClassBrowserModel::data(const QModelIndex &index, int role) const return QVariant(); if (role == Qt::DisplayRole) { if (node->statement) { - if (!(node->statement->type.isEmpty()) && - ((node->statement->kind == StatementKind::skFunction) + if (!(node->statement->type.isEmpty())) { + if ((node->statement->kind == StatementKind::skFunction) || (node->statement->kind == StatementKind::skVariable) || (node->statement->kind == StatementKind::skTypedef) - )) { - return node->statement->command + node->statement->args + " : " + node->statement->type; + ) { + return node->statement->command + node->statement->args + " : " + node->statement->type; + } else if (node->statement->kind == StatementKind::skEnum) { + if (!node->statement->value.isEmpty()) + return node->statement->command + node->statement->args + QString("(%1)").arg(node->statement->value); + else + return node->statement->command; + } } return node->statement->command + node->statement->args; } @@ -351,9 +357,12 @@ void ClassBrowserModel::filterChildren(ClassBrowserNode *node, const StatementMa if (mProcessedStatements.contains(statement.get())) continue; +// if (statement->properties.testFlag(StatementProperty::spDummyStatement)) +// continue; if (statement->kind == StatementKind::skBlock) continue; + if (statement->isInherited() && !pSettings->ui().classBrowserShowInherited()) continue; diff --git a/RedPandaIDE/widgets/codecompletionpopup.cpp b/RedPandaIDE/widgets/codecompletionpopup.cpp index 8f7ece8c..09774c37 100644 --- a/RedPandaIDE/widgets/codecompletionpopup.cpp +++ b/RedPandaIDE/widgets/codecompletionpopup.cpp @@ -286,7 +286,9 @@ void CodeCompletionPopup::addStatement(const PStatement& statement, const QStrin if (statement->kind == StatementKind::skConstructor || statement->kind == StatementKind::skDestructor || statement->kind == StatementKind::skBlock - || statement->properties.testFlag(StatementProperty::spOperatorOverloading)) + || statement->properties.testFlag(StatementProperty::spOperatorOverloading) + || statement->properties.testFlag(StatementProperty::spDummyStatement) + ) return; if ((line!=-1) && (line < statement->line)