- 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.
This commit is contained in:
Roy Qu 2023-08-07 14:23:57 +08:00
parent dd724e64c2
commit 6a06b5b3d6
7 changed files with 359 additions and 127 deletions

View File

@ -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

View File

@ -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);

View File

@ -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);i<phrases.count();i++) {
const QString& phrase=phrases[i];
if (parentStatement && parentStatement->kind == 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+1<tokenCount
&& mTokenizer[index]->text=="::"
&& 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<QString> &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 (mIndex<endIndex) {
if (mTokenizer[mIndex]->text=='+') {
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 (mIndex<endIndex) {
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 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<QString> searched;
mIndex++;
if (s=="::") {
if (mIndex>=endIndex || !isIdentChar(mTokenizer[mIndex]->text[0])) {
ok=false;
return result;
}
s+=mTokenizer[mIndex]->text;
mIndex++;
}
while (mIndex+1<endIndex
&& mTokenizer[mIndex]->text=="::"
&& 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+1<tokenCount &&
mTokenizer[mIndex+1]->text=="=") {
mIndex+=2;
if (mIndex<tokenCount) {
bool ok;
int endIndex = indexOfNextPeriodOrSemicolon(mIndex);
value = evaluateConstExpr(endIndex,ok);
if (!ok) {
canCalcValue=false;
}
} else {
if (enumStatement) {
addStatement(
enumStatement,
mCurrentFile,
lastType,
cmd,
args,
"",
"",
mTokenizer[mIndex]->line,
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<endIndex)
mIndex=endIndex;
@ -3474,6 +3703,7 @@ void CppParser::handleStructs(bool isTypedef)
PStatement scopeStatement=getCurrentScope();
QString scopelessName;
QString parentName;
if (splitLastMember(command,scopelessName,parentName)) {
scopeStatement = getIncompleteClass(parentName,getCurrentScope());
} else {
@ -3580,7 +3810,7 @@ void CppParser::handleStructs(bool isTypedef)
getCurrentScope(),
mCurrentFile,
prefix,
"__"+command,
"_@dummy@_"+command,
"",
"",
"",
@ -3589,7 +3819,7 @@ void CppParser::handleStructs(bool isTypedef)
StatementKind::skClass,
getScope(),
mCurrentMemberAccessibility,
StatementProperty::spHasDefinition);
StatementProperty::spHasDefinition | StatementProperty::spDummyStatement);
}
if (isTypedef) {
//typedef

View File

@ -216,6 +216,12 @@ private:
QStringList sortFilesByIncludeRelations(const QSet<QString> &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();

View File

@ -153,7 +153,8 @@ enum StatementProperty {
spOverride = 0x0040,
spConstexpr = 0x0080,
spFunctionPointer = 0x0100,
spOperatorOverloading = 0x0200
spOperatorOverloading = 0x0200,
spDummyStatement = 0x0400
};
Q_DECLARE_FLAGS(StatementProperties, StatementProperty)

View File

@ -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;

View File

@ -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)