diff --git a/src/libs/cplusplus/NamePrettyPrinter.cpp b/src/libs/cplusplus/NamePrettyPrinter.cpp index d78e3eaa6547e6900299b1c02deed78cf36145b4..065b7a821dda85cd12cfc545bf3163c9079dd25e 100644 --- a/src/libs/cplusplus/NamePrettyPrinter.cpp +++ b/src/libs/cplusplus/NamePrettyPrinter.cpp @@ -105,7 +105,9 @@ void NamePrettyPrinter::visit(const DestructorNameId *name) void NamePrettyPrinter::visit(const OperatorNameId *name) { - _name += QLatin1String("operator "); + _name += QLatin1String("operator"); + if (_overview->includeWhiteSpaceInOperatorName) + _name += QLatin1Char(' '); switch (name->kind()) { // ### i should probably do this in OperatorNameId case OperatorNameId::InvalidOp: _name += QLatin1String("<invalid>"); diff --git a/src/libs/cplusplus/Overview.cpp b/src/libs/cplusplus/Overview.cpp index 24838576a98638458700615107d20470eeb8b744..615b6115b27fe5b7585a3c61cfcef17908a051cb 100644 --- a/src/libs/cplusplus/Overview.cpp +++ b/src/libs/cplusplus/Overview.cpp @@ -45,6 +45,7 @@ Overview::Overview() showFunctionSignatures(true), showDefaultArguments(true), showTemplateParameters(false), + includeWhiteSpaceInOperatorName(true), markedArgument(0), markedArgumentBegin(0), markedArgumentEnd(0) diff --git a/src/libs/cplusplus/Overview.h b/src/libs/cplusplus/Overview.h index cd342e3d5598529b7354e2e058b6995d3f6792f3..77453a04b538c9088b687091e11072bc921cf74a 100644 --- a/src/libs/cplusplus/Overview.h +++ b/src/libs/cplusplus/Overview.h @@ -108,6 +108,7 @@ public: bool showFunctionSignatures: 1; bool showDefaultArguments: 1; bool showTemplateParameters: 1; + bool includeWhiteSpaceInOperatorName: 1; /// "operator =()" vs "operator=()" /*! You can get the start and end position of a function argument diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h index f86ff598084fe9feec205a7b7a284024d451e58e..356677038a919c4da9d537683ecaf99338e2e957 100644 --- a/src/plugins/cppeditor/cppeditorplugin.h +++ b/src/plugins/cppeditor/cppeditorplugin.h @@ -163,6 +163,8 @@ private slots: void test_quickfix_InsertDefFromDecl_notTriggeringWhenDefinitionExists(); void test_quickfix_InsertDefFromDecl_notTriggeringStatement(); void test_quickfix_InsertDefFromDecl_findRightImplementationFile(); + void test_quickfix_InsertDefFromDecl_respectWsInOperatorNames1(); + void test_quickfix_InsertDefFromDecl_respectWsInOperatorNames2(); void test_quickfix_InsertDeclFromDef(); @@ -202,6 +204,8 @@ private slots: void test_quickfix_MoveFuncDefOutside_CtorWithInitialization1(); void test_quickfix_MoveFuncDefOutside_CtorWithInitialization2(); void test_quickfix_MoveFuncDefOutside_afterClass(); + void test_quickfix_MoveFuncDefOutside_respectWsInOperatorNames1(); + void test_quickfix_MoveFuncDefOutside_respectWsInOperatorNames2(); void test_quickfix_MoveFuncDefToDecl_MemberFunc(); void test_quickfix_MoveFuncDefToDecl_MemberFuncOutside(); diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index 5969e36d9bb4e537a9af719bbc11d8fb13967341..0bd5554f22b092cbd85c8d06d0834bf71224d969 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -1178,6 +1178,58 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_findRightImplementationFil data.run(&factory); } +/// Check if whitespace is respected for operator functions +void CppEditorPlugin::test_quickfix_InsertDefFromDecl_respectWsInOperatorNames1() +{ + QByteArray original = + "class Foo\n" + "{\n" + " Foo &opera@tor =();\n" + "};\n"; + QByteArray expected = + "class Foo\n" + "{\n" + " Foo &operator =();\n" + "};\n" + "\n" + "\n" + "Foo &Foo::operator =()\n" + "{\n" + "\n" + "}\n" + "\n"; + + InsertDefFromDecl factory; + TestCase data(original, expected); + data.run(&factory); +} + +/// Check if whitespace is respected for operator functions +void CppEditorPlugin::test_quickfix_InsertDefFromDecl_respectWsInOperatorNames2() +{ + QByteArray original = + "class Foo\n" + "{\n" + " Foo &opera@tor=();\n" + "};\n"; + QByteArray expected = + "class Foo\n" + "{\n" + " Foo &operator=();\n" + "};\n" + "\n" + "\n" + "Foo &Foo::operator=()\n" + "{\n" + "\n" + "}\n" + "\n"; + + InsertDefFromDecl factory; + TestCase data(original, expected); + data.run(&factory); +} + // Function for one of InsertDeclDef section cases void insertToSectionDeclFromDef(const QByteArray §ion, int sectionIndex) { @@ -2458,6 +2510,52 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_afterClass() data.run(&factory, 1); } +/// Check if whitespace is respected for operator functions +void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_respectWsInOperatorNames1() +{ + QByteArray original = + "class Foo\n" + "{\n" + " Foo &opera@tor =() {}\n" + "};\n"; + QByteArray expected = + "class Foo\n" + "{\n" + " Foo &operator =();\n" + "};\n" + "\n" + "\n" + "Foo &Foo::operator =() {}\n" + "\n"; + + MoveFuncDefOutside factory; + TestCase data(original, expected); + data.run(&factory); +} + +/// Check if whitespace is respected for operator functions +void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_respectWsInOperatorNames2() +{ + QByteArray original = + "class Foo\n" + "{\n" + " Foo &opera@tor=() {}\n" + "};\n"; + QByteArray expected = + "class Foo\n" + "{\n" + " Foo &operator=();\n" + "};\n" + "\n" + "\n" + "Foo &Foo::operator=() {}\n" + "\n"; + + MoveFuncDefOutside factory; + TestCase data(original, expected); + data.run(&factory); +} + /// Check: revert test_quickfix_MoveFuncDefOutside_MemberFuncToCpp() void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_MemberFunc() { diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 19e4665d80cd3a9a6a1a128e085029aceb9ac35e..14d8db4ed90df74231b732687d8325de353b9552 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -258,6 +258,12 @@ void insertNewIncludeDirective(const QString &include, CppRefactoringFilePtr fil file->apply(); } +bool nameIncludesOperatorName(const Name *name) +{ + return name->isOperatorNameId() + || (name->isQualifiedNameId() && name->asQualifiedNameId()->name()->isOperatorNameId()); +} + } // anonymous namespace namespace { @@ -2482,10 +2488,12 @@ class InsertDefOperation: public CppQuickFixOperation public: // Make sure that either loc is valid or targetFileName is not empty. InsertDefOperation(const QSharedPointer<const CppQuickFixAssistInterface> &interface, - Declaration *decl, const InsertionLocation &loc, const DefPos defpos, - const QString &targetFileName = QString(), bool freeFunction = false) + Declaration *decl, DeclaratorAST *declAST, const InsertionLocation &loc, + const DefPos defpos, const QString &targetFileName = QString(), + bool freeFunction = false) : CppQuickFixOperation(interface, 0) , m_decl(decl) + , m_declAST(declAST) , m_loc(loc) , m_defpos(defpos) , m_targetFileName(targetFileName) @@ -2558,6 +2566,11 @@ public: const FullySpecifiedType tn = rewriteType(m_decl->type(), &env, control); // rewrite the function name + if (nameIncludesOperatorName(m_decl->name())) { + CppRefactoringFilePtr file = refactoring.file(fileName()); + const QString operatorNameText = file->textOf(m_declAST->core_declarator); + oo.includeWhiteSpaceInOperatorName = operatorNameText.contains(QLatin1Char(' ')); + } const QString name = oo.prettyName(LookupContext::minimalName(m_decl, targetCoN, control)); @@ -2590,6 +2603,7 @@ public: private: Declaration *m_decl; + DeclaratorAST *m_declAST; InsertionLocation m_loc; const DefPos m_defpos; const QString m_targetFileName; @@ -2622,6 +2636,7 @@ void InsertDefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOpe } // Insert Position: Implementation File + DeclaratorAST *declAST = simpleDecl->declarator_list->value; InsertDefOperation *op = 0; ProjectFile::Kind kind = ProjectFile::classify(interface->fileName()); const bool isHeaderFile = ProjectFile::isHeader(kind); @@ -2634,7 +2649,7 @@ void InsertDefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOpe foreach (const InsertionLocation &location, locator.methodDefinition(decl, false, QString())) { if (location.isValid()) { - op = new InsertDefOperation(interface, decl, + op = new InsertDefOperation(interface, decl, declAST, InsertionLocation(), DefPosImplementationFile, location.fileName()); @@ -2649,8 +2664,8 @@ void InsertDefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOpe // Insert Position: Outside Class if (!isFreeFunction) { - op = new InsertDefOperation(interface, decl, InsertionLocation(), - DefPosOutsideClass, + op = new InsertDefOperation(interface, decl, declAST, + InsertionLocation(), DefPosOutsideClass, interface->fileName()); result.append(CppQuickFixOperation::Ptr(op)); } @@ -2663,8 +2678,9 @@ void InsertDefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOpe const InsertionLocation loc = InsertionLocation(interface->fileName(), QString(), QString(), line, column); - op = new InsertDefOperation(interface, decl, loc, DefPosInsideClass, - QString() , isFreeFunction); + op = new InsertDefOperation(interface, decl, declAST, loc, + DefPosInsideClass, QString(), + isFreeFunction); result.append(CppQuickFixOperation::Ptr(op)); return; @@ -3719,14 +3735,19 @@ void ApplyDeclDefLinkChanges::match(const CppQuickFixInterface &interface, namespace { -QString getDefinitionSignature(const CppQuickFixAssistInterface *assist, Function *func, - CppRefactoringFilePtr &file, Scope *scope) +QString definitionSignature(const CppQuickFixAssistInterface *assist, + FunctionDefinitionAST *functionDefinitionAST, + CppRefactoringFilePtr &baseFile, + CppRefactoringFilePtr &targetFile, + Scope *scope) { QTC_ASSERT(assist, return QString()); - QTC_ASSERT(func, return QString()); + QTC_ASSERT(functionDefinitionAST, return QString()); QTC_ASSERT(scope, return QString()); + Function *func = functionDefinitionAST->symbol; + QTC_ASSERT(func, return QString()); - LookupContext cppContext(file->cppDocument(), assist->snapshot()); + LookupContext cppContext(targetFile->cppDocument(), assist->snapshot()); ClassOrNamespace *cppCoN = cppContext.lookupType(scope); if (!cppCoN) cppCoN = cppContext.globalNamespace(); @@ -3740,10 +3761,16 @@ QString getDefinitionSignature(const CppQuickFixAssistInterface *assist, Functio oo.showFunctionSignatures = true; oo.showReturnTypes = true; oo.showArgumentNames = true; + const Name *name = func->name(); + if (nameIncludesOperatorName(name)) { + CoreDeclaratorAST *coreDeclarator = functionDefinitionAST->declarator->core_declarator; + const QString operatorNameText = baseFile->textOf(coreDeclarator); + oo.includeWhiteSpaceInOperatorName = operatorNameText.contains(QLatin1Char(' ')); + } + const QString nameText = oo.prettyName(LookupContext::minimalName(func, cppCoN, control)); const FullySpecifiedType tn = rewriteType(func->type(), &env, control); - const QString name = oo.prettyName(LookupContext::minimalName(func, cppCoN, control)); - return oo.prettyType(tn, name); + return oo.prettyType(tn, nameText); } class MoveFuncDefOutsideOp : public CppQuickFixOperation @@ -3791,8 +3818,9 @@ public: Scope *scopeAtInsertPos = toFile->cppDocument()->scopeAt(l.line(), l.column()); // construct definition - const QString funcDec = getDefinitionSignature(assistInterface(), m_func, toFile, - scopeAtInsertPos); + const QString funcDec = definitionSignature(assistInterface(), m_funcDef, + fromFile, toFile, + scopeAtInsertPos); QString funcDef = prefix + funcDec; const int startPosition = fromFile->endOf(m_funcDef->declarator); const int endPosition = fromFile->endOf(m_funcDef->function_body); diff --git a/src/plugins/cpptools/cpppointerdeclarationformatter.cpp b/src/plugins/cpptools/cpppointerdeclarationformatter.cpp index bb4291b5e7cc9277db1feeeecadf5d879514baca..edcb105028966d20f706745a86ec2970c0acd850 100644 --- a/src/plugins/cpptools/cpppointerdeclarationformatter.cpp +++ b/src/plugins/cpptools/cpppointerdeclarationformatter.cpp @@ -115,7 +115,7 @@ static unsigned firstTypeSpecifierWithoutFollowingAttribute( PointerDeclarationFormatter::PointerDeclarationFormatter( const CppRefactoringFilePtr refactoringFile, - const Overview &overview, + Overview &overview, CursorHandling cursorHandling) : ASTVisitor(refactoringFile->cppDocument()->translationUnit()) , m_cppRefactoringFile(refactoringFile) @@ -413,21 +413,14 @@ void PointerDeclarationFormatter::checkAndRewrite(DeclaratorAST *declarator, QString rewrittenDeclaration; const Name *name = symbol->name(); if (name) { - if (name->isOperatorNameId()) { - // Take the operator name from the file instead from the AST, so the white - // spaces within the operator names can be respected, e.g. in "operator =". - const QByteArray operatorText - = m_cppRefactoringFile->textOf(declarator->core_declarator).toLatin1(); - Identifier operatorName(operatorText.constData(), operatorText.size()); - rewrittenDeclaration = rewriteDeclaration(type, &operatorName); - } else { - rewrittenDeclaration = rewriteDeclaration(type, name); + if (name->isOperatorNameId() + || (name->isQualifiedNameId() + && name->asQualifiedNameId()->name()->isOperatorNameId())) { + const QString operatorText = m_cppRefactoringFile->textOf(declarator->core_declarator); + m_overview.includeWhiteSpaceInOperatorName = operatorText.contains(QLatin1Char(' ')); } - } else { - // The declaration will be correctly rewritten for name == 0 (e.g. "int *"). - rewrittenDeclaration = rewriteDeclaration(type, name); } - + rewrittenDeclaration = m_overview.prettyType(type, name); rewrittenDeclaration.remove(0, charactersToRemove); CHECK_R(originalDeclaration != rewrittenDeclaration, "Rewritten is same as original"); @@ -460,21 +453,6 @@ void PointerDeclarationFormatter::checkAndRewrite(DeclaratorAST *declarator, qDebug() << "Replacement operation failed"; } -/*! Rewrite/format the given type and name. */ -QString PointerDeclarationFormatter::rewriteDeclaration(FullySpecifiedType type, const Name *name) - const -{ - CHECK_RV(type.isValid(), "Invalid type", QString()); - - const char *identifier = 0; - if (const Name *declarationName = name) { - if (const Identifier *id = declarationName->identifier()) - identifier = id->chars(); - } - - return m_overview.prettyType(type, QLatin1String(identifier)); -} - void PointerDeclarationFormatter::printCandidate(AST *ast) { #if DEBUG_OUTPUT diff --git a/src/plugins/cpptools/cpppointerdeclarationformatter.h b/src/plugins/cpptools/cpppointerdeclarationformatter.h index 0419f8257ef9d4ee856215cea79da09e532b01c6..621c6d3971823e974fa3881963584c3e569e7f7d 100644 --- a/src/plugins/cpptools/cpppointerdeclarationformatter.h +++ b/src/plugins/cpptools/cpppointerdeclarationformatter.h @@ -78,7 +78,7 @@ public: enum CursorHandling { RespectCursor, IgnoreCursor }; explicit PointerDeclarationFormatter(const CppRefactoringFilePtr refactoringFile, - const Overview &overview, + Overview &overview, CursorHandling cursorHandling = IgnoreCursor); /*! @@ -113,11 +113,10 @@ private: void processIfWhileForStatement(ExpressionAST *expression, Symbol *symbol); void checkAndRewrite(DeclaratorAST *declarator, Symbol *symbol, TokenRange range, unsigned charactersToRemove = 0); - QString rewriteDeclaration(FullySpecifiedType type, const Name *name) const; void printCandidate(AST *ast); const CppRefactoringFilePtr m_cppRefactoringFile; - const Overview &m_overview; + Overview &m_overview; const CursorHandling m_cursorHandling; ChangeSet m_changeSet; diff --git a/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp b/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp index 474a829ac53da1876a493689146c38b899a8d280..cddb4d0db26cc121b24440393b88602901771c8e 100644 --- a/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp +++ b/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp @@ -349,9 +349,13 @@ void CppToolsPlugin::test_format_pointerdeclaration_in_simpledeclarations_data() << source << stripCursor(source); // Respect white space within operator names - QTest::newRow("operators") + QTest::newRow("operators1") << "class C { C@&operator = (const C &); };" << "class C { C & operator = (const C &); };"; + + QTest::newRow("operators2") + << "C &C::operator = (const C &) {}" + << "C & C::operator = (const C &) {}"; } void CppToolsPlugin::test_format_pointerdeclaration_in_controlflowstatements()