diff options
author | Nikolai Kosjar <nikolai.kosjar@digia.com> | 2013-02-07 12:16:41 +0100 |
---|---|---|
committer | Nikolai Kosjar <nikolai.kosjar@digia.com> | 2013-02-14 12:25:20 +0100 |
commit | c6a7fb8ccf82ae69be615c4672a790229dff995c (patch) | |
tree | 5bfa9c05e573730450c519e670a95ab13288e4e8 /src/plugins/cppeditor | |
parent | 986fc8bfc93c87451c6532f61c407e3459feccc1 (diff) | |
download | qt-creator-c6a7fb8ccf82ae69be615c4672a790229dff995c.tar.gz |
C++: Refactor quick fixes
- Put declarations into quickfixes.h to simplify testing
- Give the factories more meaningful names
Change-Id: If74c29a8c17819d5369ffa3df94d146b14e53af9
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
Diffstat (limited to 'src/plugins/cppeditor')
-rw-r--r-- | src/plugins/cppeditor/cppeditor.pro | 1 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppeditor.qbs | 1 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppinsertdecldef.cpp | 14 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppinsertdecldef.h | 8 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppplugin.cpp | 3 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppplugin.h | 24 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppquickfix_test.cpp | 48 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppquickfixes.cpp | 2981 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppquickfixes.h | 393 |
9 files changed, 1794 insertions, 1679 deletions
diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index 604246173b..b7fa70a6f7 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -22,6 +22,7 @@ HEADERS += cppplugin.h \ cppinsertqtpropertymembers.h \ cppquickfixassistant.h \ cppquickfix.h \ + cppquickfixes.h \ cppfunctiondecldeflink.h SOURCES += cppplugin.cpp \ diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs index 7f92612944..96540ea650 100644 --- a/src/plugins/cppeditor/cppeditor.qbs +++ b/src/plugins/cppeditor/cppeditor.qbs @@ -54,6 +54,7 @@ QtcPlugin { "cppquickfixassistant.cpp", "cppquickfixassistant.h", "cppquickfixes.cpp", + "cppquickfixes.h", "cppsnippetprovider.cpp", "cppsnippetprovider.h", "cpptypehierarchy.cpp", diff --git a/src/plugins/cppeditor/cppinsertdecldef.cpp b/src/plugins/cppeditor/cppinsertdecldef.cpp index 2e8a788cad..cf534e9d21 100644 --- a/src/plugins/cppeditor/cppinsertdecldef.cpp +++ b/src/plugins/cppeditor/cppinsertdecldef.cpp @@ -151,7 +151,7 @@ Class *isMemberFunction(const LookupContext &context, Function *function) } // anonymous namespace -void DeclFromDef::match(const CppQuickFixInterface &interface, QuickFixOperations &result) +void InsertDeclFromDef::match(const CppQuickFixInterface &interface, QuickFixOperations &result) { const QList<AST *> &path = interface->path(); CppRefactoringFilePtr file = interface->currentFile(); @@ -287,7 +287,7 @@ private: } // anonymous namespace -void DefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOperations &result) +void InsertDefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOperations &result) { const QList<AST *> &path = interface->path(); @@ -319,11 +319,11 @@ void DefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOperation namespace { -class GetterSetterOperation : public CppQuickFixOperation +class GenerateGetterSetterOperation : public CppQuickFixOperation { public: - GetterSetterOperation(const QSharedPointer<const CppQuickFixAssistInterface> &interface, - bool testMode = false) + GenerateGetterSetterOperation(const QSharedPointer<const CppQuickFixAssistInterface> &interface, + bool testMode = false) : CppQuickFixOperation(interface) , m_variableName(0) , m_declaratorId(0) @@ -602,9 +602,9 @@ public: } // namespace -void GetterSetter::match(const CppQuickFixInterface &interface, QuickFixOperations &result) +void GenerateGetterSetter::match(const CppQuickFixInterface &interface, QuickFixOperations &result) { - GetterSetterOperation *op = new GetterSetterOperation(interface, m_testMode); + GenerateGetterSetterOperation *op = new GenerateGetterSetterOperation(interface, m_testMode); if (op->isValid()) result.append(CppQuickFixOperation::Ptr(op)); else diff --git a/src/plugins/cppeditor/cppinsertdecldef.h b/src/plugins/cppeditor/cppinsertdecldef.h index 75d4488d69..d73dd67cd7 100644 --- a/src/plugins/cppeditor/cppinsertdecldef.h +++ b/src/plugins/cppeditor/cppinsertdecldef.h @@ -35,13 +35,13 @@ namespace CppEditor { namespace Internal { -class DeclFromDef: public CppQuickFixFactory +class InsertDeclFromDef: public CppQuickFixFactory { public: void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result); }; -class DefFromDecl: public CppQuickFixFactory +class InsertDefFromDecl: public CppQuickFixFactory { public: void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result); @@ -53,10 +53,10 @@ public: void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result); }; -class GetterSetter : public CppQuickFixFactory +class GenerateGetterSetter : public CppQuickFixFactory { public: - GetterSetter(const bool testMode = false) : m_testMode(testMode) {} + GenerateGetterSetter(const bool testMode = false) : m_testMode(testMode) {} void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result); private: const bool m_testMode; diff --git a/src/plugins/cppeditor/cppplugin.cpp b/src/plugins/cppeditor/cppplugin.cpp index 0210d8a8df..eac6390c0b 100644 --- a/src/plugins/cppeditor/cppplugin.cpp +++ b/src/plugins/cppeditor/cppplugin.cpp @@ -38,6 +38,7 @@ #include "cpptypehierarchy.h" #include "cppsnippetprovider.h" #include "cppquickfixassistant.h" +#include "cppquickfixes.h" #include <coreplugin/icore.h> #include <coreplugin/coreconstants.h> @@ -187,7 +188,7 @@ bool CppPlugin::initialize(const QStringList & /*arguments*/, QString *errorMess m_quickFixProvider = new CppQuickFixAssistProvider; addAutoReleasedObject(m_quickFixProvider); - registerQuickFixes(this); + CppEditor::Internal::registerQuickFixes(this); QObject *core = Core::ICore::instance(); CppFileWizard::BaseFileWizardParameters wizardParameters(Core::IWizard::FileWizard); diff --git a/src/plugins/cppeditor/cppplugin.h b/src/plugins/cppeditor/cppplugin.h index fca9c54de8..5f3b2f70c4 100644 --- a/src/plugins/cppeditor/cppplugin.h +++ b/src/plugins/cppeditor/cppplugin.h @@ -90,18 +90,18 @@ private slots: #ifdef WITH_TESTS private slots: // quickfix tests - void test_quickfix_GetterSetter_basicGetterWithPrefix(); - void test_quickfix_GetterSetter_basicGetterWithoutPrefix(); - void test_quickfix_GetterSetter_customType(); - void test_quickfix_GetterSetter_constMember(); - void test_quickfix_GetterSetter_pointerToNonConst(); - void test_quickfix_GetterSetter_pointerToConst(); - void test_quickfix_GetterSetter_staticMember(); - void test_quickfix_GetterSetter_secondDeclarator(); - void test_quickfix_GetterSetter_triggeringRightAfterPointerSign(); - void test_quickfix_GetterSetter_notTriggeringOnMemberFunction(); - void test_quickfix_GetterSetter_notTriggeringOnMemberArray(); - void test_quickfix_GetterSetter_notTriggeringWhenGetterOrSetterExist(); + void test_quickfix_GenerateGetterSetter_basicGetterWithPrefix(); + void test_quickfix_GenerateGetterSetter_basicGetterWithoutPrefix(); + void test_quickfix_GenerateGetterSetter_customType(); + void test_quickfix_GenerateGetterSetter_constMember(); + void test_quickfix_GenerateGetterSetter_pointerToNonConst(); + void test_quickfix_GenerateGetterSetter_pointerToConst(); + void test_quickfix_GenerateGetterSetter_staticMember(); + void test_quickfix_GenerateGetterSetter_secondDeclarator(); + void test_quickfix_GenerateGetterSetter_triggeringRightAfterPointerSign(); + void test_quickfix_GenerateGetterSetter_notTriggeringOnMemberFunction(); + void test_quickfix_GenerateGetterSetter_notTriggeringOnMemberArray(); + void test_quickfix_GenerateGetterSetter_notTriggeringWhenGetterOrSetterExist(); #endif // WITH_TESTS private: diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index 45f49f66c0..f134043a8c 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -194,7 +194,7 @@ void TestCase::run(CppQuickFixFactory *factory, const QByteArray &expected, /// 1. If the name does not start with ("m_" or "_") and does not /// end with "_", we are forced to prefix the getter with "get". /// 2. Setter: Use pass by value on integer/float and pointer types. -void CppPlugin::test_quickfix_GetterSetter_basicGetterWithPrefix() +void CppPlugin::test_quickfix_GenerateGetterSetter_basicGetterWithPrefix() { TestCase data("\n" "class Something\n" @@ -224,14 +224,14 @@ void CppPlugin::test_quickfix_GetterSetter_basicGetterWithPrefix() "\n" ; - GetterSetter factory(/*testMode=*/ true); + GenerateGetterSetter factory(/*testMode=*/ true); data.run(&factory, expected); } /// Checks: /// 1. Getter: "get" prefix is not necessary. /// 2. Setter: Parameter name is base name. -void CppPlugin::test_quickfix_GetterSetter_basicGetterWithoutPrefix() +void CppPlugin::test_quickfix_GenerateGetterSetter_basicGetterWithoutPrefix() { TestCase data("\n" "class Something\n" @@ -261,13 +261,13 @@ void CppPlugin::test_quickfix_GetterSetter_basicGetterWithoutPrefix() "\n" ; - GetterSetter factory(/*testMode=*/ true); + GenerateGetterSetter factory(/*testMode=*/ true); data.run(&factory, expected); } /// Check: Setter: Use pass by reference for parameters which /// are not integer, float or pointers. -void CppPlugin::test_quickfix_GetterSetter_customType() +void CppPlugin::test_quickfix_GenerateGetterSetter_customType() { TestCase data("\n" "class Something\n" @@ -297,14 +297,14 @@ void CppPlugin::test_quickfix_GetterSetter_customType() "\n" ; - GetterSetter factory(/*testMode=*/ true); + GenerateGetterSetter factory(/*testMode=*/ true); data.run(&factory, expected); } /// Checks: /// 1. Setter: No setter is generated for const members. /// 2. Getter: Return a non-const type since it pass by value anyway. -void CppPlugin::test_quickfix_GetterSetter_constMember() +void CppPlugin::test_quickfix_GenerateGetterSetter_constMember() { TestCase data("\n" "class Something\n" @@ -328,12 +328,12 @@ void CppPlugin::test_quickfix_GetterSetter_constMember() "\n" ; - GetterSetter factory(/*testMode=*/ true); + GenerateGetterSetter factory(/*testMode=*/ true); data.run(&factory, expected); } /// Checks: No special treatment for pointer to non const. -void CppPlugin::test_quickfix_GetterSetter_pointerToNonConst() +void CppPlugin::test_quickfix_GenerateGetterSetter_pointerToNonConst() { TestCase data("\n" "class Something\n" @@ -363,12 +363,12 @@ void CppPlugin::test_quickfix_GetterSetter_pointerToNonConst() "\n" ; - GetterSetter factory(/*testMode=*/ true); + GenerateGetterSetter factory(/*testMode=*/ true); data.run(&factory, expected); } /// Checks: No special treatment for pointer to const. -void CppPlugin::test_quickfix_GetterSetter_pointerToConst() +void CppPlugin::test_quickfix_GenerateGetterSetter_pointerToConst() { TestCase data("\n" "class Something\n" @@ -398,14 +398,14 @@ void CppPlugin::test_quickfix_GetterSetter_pointerToConst() "\n" ; - GetterSetter factory(/*testMode=*/ true); + GenerateGetterSetter factory(/*testMode=*/ true); data.run(&factory, expected); } /// Checks: /// 1. Setter: Setter is a static function. /// 2. Getter: Getter is a static, non const function. -void CppPlugin::test_quickfix_GetterSetter_staticMember() +void CppPlugin::test_quickfix_GenerateGetterSetter_staticMember() { TestCase data("\n" "class Something\n" @@ -435,12 +435,12 @@ void CppPlugin::test_quickfix_GetterSetter_staticMember() "\n" ; - GetterSetter factory(/*testMode=*/ true); + GenerateGetterSetter factory(/*testMode=*/ true); data.run(&factory, expected); } /// Check: Check if it works on the second declarator -void CppPlugin::test_quickfix_GetterSetter_secondDeclarator() +void CppPlugin::test_quickfix_GenerateGetterSetter_secondDeclarator() { TestCase data("\n" "class Something\n" @@ -470,12 +470,12 @@ void CppPlugin::test_quickfix_GetterSetter_secondDeclarator() "\n" ; - GetterSetter factory(/*testMode=*/ true); + GenerateGetterSetter factory(/*testMode=*/ true); data.run(&factory, expected); } /// Check: Quick fix is offered for "int *@it;" ('@' denotes the text cursor position) -void CppPlugin::test_quickfix_GetterSetter_triggeringRightAfterPointerSign() +void CppPlugin::test_quickfix_GenerateGetterSetter_triggeringRightAfterPointerSign() { TestCase data("\n" "class Something\n" @@ -505,33 +505,33 @@ void CppPlugin::test_quickfix_GetterSetter_triggeringRightAfterPointerSign() "\n" ; - GetterSetter factory(/*testMode=*/ true); + GenerateGetterSetter factory(/*testMode=*/ true); data.run(&factory, expected); } /// Check: Quick fix is not triggered on a member function. -void CppPlugin::test_quickfix_GetterSetter_notTriggeringOnMemberFunction() +void CppPlugin::test_quickfix_GenerateGetterSetter_notTriggeringOnMemberFunction() { TestCase data("class Something { void @f(); };"); QByteArray expected = data.originalText; - GetterSetter factory(/*testMode=*/ true); + GenerateGetterSetter factory(/*testMode=*/ true); data.run(&factory, expected, /*changesExpected=*/ false); } /// Check: Quick fix is not triggered on an member array; -void CppPlugin::test_quickfix_GetterSetter_notTriggeringOnMemberArray() +void CppPlugin::test_quickfix_GenerateGetterSetter_notTriggeringOnMemberArray() { TestCase data("class Something { void @a[10]; };"); QByteArray expected = data.originalText; - GetterSetter factory(/*testMode=*/ true); + GenerateGetterSetter factory(/*testMode=*/ true); data.run(&factory, expected, /*changesExpected=*/ false); } /// Check: Do not offer the quick fix if there is already a member with the /// getter or setter name we would generate. -void CppPlugin::test_quickfix_GetterSetter_notTriggeringWhenGetterOrSetterExist() +void CppPlugin::test_quickfix_GenerateGetterSetter_notTriggeringWhenGetterOrSetterExist() { TestCase data("\n" "class Something {\n" @@ -540,6 +540,6 @@ void CppPlugin::test_quickfix_GetterSetter_notTriggeringWhenGetterOrSetterExist( "};\n"); QByteArray expected = data.originalText; - GetterSetter factory(/*testMode=*/ true); + GenerateGetterSetter factory(/*testMode=*/ true); data.run(&factory, expected, /*changesExpected=*/ false); } diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 288cbd87da..9967ef84fe 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -27,20 +27,14 @@ ** ****************************************************************************/ -#include "cppcompleteswitch.h" +#include "cppquickfixes.h" + #include "cppcompleteswitch.h" #include "cppeditor.h" -#include "cppfunctiondecldeflink.h" #include "cppinsertdecldef.h" #include "cppinsertqtpropertymembers.h" #include "cppquickfixassistant.h" -#include "cppquickfix.h" -#include <AST.h> -#include <ASTMatcher.h> -#include <ASTPatternBuilder.h> -#include <ASTVisitor.h> -#include <CoreTypes.h> #include <Literals.h> #include <Name.h> #include <Names.h> @@ -48,7 +42,6 @@ #include <Symbols.h> #include <Token.h> #include <TranslationUnit.h> -#include <Type.h> #include <cplusplus/CppRewriter.h> #include <cplusplus/DependencyTable.h> @@ -57,29 +50,58 @@ #include <cpptools/cppclassesfilter.h> #include <cpptools/cppcodestylesettings.h> #include <cpptools/cpppointerdeclarationformatter.h> -#include <cpptools/cpprefactoringchanges.h> #include <cpptools/cpptoolsconstants.h> -#include <cpptools/cpptoolsreuse.h> -#include <cpptools/insertionpointlocator.h> #include <cpptools/ModelManagerInterface.h> -#include <cpptools/searchsymbols.h> #include <cpptools/symbolfinder.h> -#include <extensionsystem/iplugin.h> #include <extensionsystem/pluginmanager.h> +#include <utils/changeset.h> #include <utils/qtcassert.h> -#include <cctype> #include <QApplication> #include <QFileInfo> +#include <QSharedPointer> #include <QTextBlock> #include <QTextCursor> using namespace CppEditor; using namespace CppEditor::Internal; -using namespace CppTools; -using namespace CPlusPlus; -using namespace TextEditor; -using namespace Utils; + +void CppEditor::Internal::registerQuickFixes(ExtensionSystem::IPlugin *plugIn) +{ + plugIn->addAutoReleasedObject(new AddIncludeForUndefinedIdentifier); + plugIn->addAutoReleasedObject(new AddIncludeForForwardDeclaration); + + plugIn->addAutoReleasedObject(new FlipLogicalOperands); + plugIn->addAutoReleasedObject(new InverseLogicalComparison); + plugIn->addAutoReleasedObject(new RewriteLogicalAnd); + + plugIn->addAutoReleasedObject(new ConvertToCamelCase); + + plugIn->addAutoReleasedObject(new ConvertCStringToNSString); + plugIn->addAutoReleasedObject(new ConvertNumericLiteral); + plugIn->addAutoReleasedObject(new TranslateStringLiteral); + plugIn->addAutoReleasedObject(new WrapStringLiteral); + + plugIn->addAutoReleasedObject(new MoveDeclarationOutOfIf); + plugIn->addAutoReleasedObject(new MoveDeclarationOutOfWhile); + + plugIn->addAutoReleasedObject(new SplitIfStatement); + plugIn->addAutoReleasedObject(new SplitSimpleDeclaration); + + plugIn->addAutoReleasedObject(new AddLocalDeclaration); + plugIn->addAutoReleasedObject(new AddBracesToIf); + plugIn->addAutoReleasedObject(new RearrangeParamDeclarationList); + plugIn->addAutoReleasedObject(new ReformatPointerDeclaration); + + plugIn->addAutoReleasedObject(new CompleteSwitchCaseStatement); + plugIn->addAutoReleasedObject(new InsertQtPropertyMembers); + + plugIn->addAutoReleasedObject(new ApplyDeclDefLinkChanges); + plugIn->addAutoReleasedObject(new ExtractFunction); + plugIn->addAutoReleasedObject(new GenerateGetterSetter); + plugIn->addAutoReleasedObject(new InsertDeclFromDef); + plugIn->addAutoReleasedObject(new InsertDefFromDecl); +} static inline bool isQtStringLiteral(const QByteArray &id) { @@ -91,838 +113,702 @@ static inline bool isQtStringTranslation(const QByteArray &id) return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP"; } -namespace { - -/* - Rewrite - a op b -> !(a invop b) - (a op b) -> !(a invop b) - !(a op b) -> (a invob b) - - Activates on: <= < > >= == != -*/ -class UseInverseOp: public CppQuickFixFactory +class InverseLogicalComparisonOp: public CppQuickFixOperation { public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) + InverseLogicalComparisonOp(const CppQuickFixInterface &interface, int priority, + BinaryExpressionAST *binary, Kind invertToken) + : CppQuickFixOperation(interface, priority) + , binary(binary), nested(0), negation(0) { - CppRefactoringFilePtr file = interface->currentFile(); + Token tok; + tok.f.kind = invertToken; + replacement = QLatin1String(tok.spell()); - const QList<AST *> &path = interface->path(); - int index = path.size() - 1; - BinaryExpressionAST *binary = path.at(index)->asBinaryExpression(); - if (! binary) - return; - if (! interface->isCursorOn(binary->binary_op_token)) - return; + // check for enclosing nested expression + if (priority - 1 >= 0) + nested = interface->path()[priority - 1]->asNestedExpression(); - Kind invertToken; - switch (file->tokenAt(binary->binary_op_token).kind()) { - case T_LESS_EQUAL: - invertToken = T_GREATER; - break; - case T_LESS: - invertToken = T_GREATER_EQUAL; - break; - case T_GREATER: - invertToken = T_LESS_EQUAL; - break; - case T_GREATER_EQUAL: - invertToken = T_LESS; - break; - case T_EQUAL_EQUAL: - invertToken = T_EXCLAIM_EQUAL; - break; - case T_EXCLAIM_EQUAL: - invertToken = T_EQUAL_EQUAL; - break; - default: - return; + // check for ! before parentheses + if (nested && priority - 2 >= 0) { + negation = interface->path()[priority - 2]->asUnaryExpression(); + if (negation && ! interface->currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM)) + negation = 0; } - - result.append(CppQuickFixOperation::Ptr(new Operation(interface, index, binary, invertToken))); } -private: - class Operation: public CppQuickFixOperation + QString description() const { - BinaryExpressionAST *binary; - NestedExpressionAST *nested; - UnaryExpressionAST *negation; - - QString replacement; + return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement); + } - public: - Operation(const CppQuickFixInterface &interface, - int priority, BinaryExpressionAST *binary, Kind invertToken) - : CppQuickFixOperation(interface, priority) - , binary(binary), nested(0), negation(0) - { - Token tok; - tok.f.kind = invertToken; - replacement = QLatin1String(tok.spell()); - - // check for enclosing nested expression - if (priority - 1 >= 0) - nested = interface->path()[priority - 1]->asNestedExpression(); - - // check for ! before parentheses - if (nested && priority - 2 >= 0) { - negation = interface->path()[priority - 2]->asUnaryExpression(); - if (negation && ! interface->currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM)) - negation = 0; - } + void perform() + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + + ChangeSet changes; + if (negation) { + // can't remove parentheses since that might break precedence + changes.remove(currentFile->range(negation->unary_op_token)); + } else if (nested) { + changes.insert(currentFile->startOf(nested), QLatin1String("!")); + } else { + changes.insert(currentFile->startOf(binary), QLatin1String("!(")); + changes.insert(currentFile->endOf(binary), QLatin1String(")")); } + changes.replace(currentFile->range(binary->binary_op_token), replacement); + currentFile->setChangeSet(changes); + currentFile->apply(); + } - virtual QString description() const - { - return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement); - } +private: + BinaryExpressionAST *binary; + NestedExpressionAST *nested; + UnaryExpressionAST *negation; - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - - ChangeSet changes; - if (negation) { - // can't remove parentheses since that might break precedence - changes.remove(currentFile->range(negation->unary_op_token)); - } else if (nested) { - changes.insert(currentFile->startOf(nested), QLatin1String("!")); - } else { - changes.insert(currentFile->startOf(binary), QLatin1String("!(")); - changes.insert(currentFile->endOf(binary), QLatin1String(")")); - } - changes.replace(currentFile->range(binary->binary_op_token), replacement); - currentFile->setChangeSet(changes); - currentFile->apply(); - } - }; + QString replacement; }; -/* - Rewrite - a op b +void InverseLogicalComparison::match(const CppQuickFixInterface &interface, + QuickFixOperations &result) +{ + CppRefactoringFilePtr file = interface->currentFile(); + + const QList<AST *> &path = interface->path(); + int index = path.size() - 1; + BinaryExpressionAST *binary = path.at(index)->asBinaryExpression(); + if (! binary) + return; + if (! interface->isCursorOn(binary->binary_op_token)) + return; + + Kind invertToken; + switch (file->tokenAt(binary->binary_op_token).kind()) { + case T_LESS_EQUAL: + invertToken = T_GREATER; + break; + case T_LESS: + invertToken = T_GREATER_EQUAL; + break; + case T_GREATER: + invertToken = T_LESS_EQUAL; + break; + case T_GREATER_EQUAL: + invertToken = T_LESS; + break; + case T_EQUAL_EQUAL: + invertToken = T_EXCLAIM_EQUAL; + break; + case T_EXCLAIM_EQUAL: + invertToken = T_EQUAL_EQUAL; + break; + default: + return; + } - As - b flipop a + result.append(CppQuickFixOperation::Ptr( + new InverseLogicalComparisonOp(interface, index, binary, invertToken))); +} - Activates on: <= < > >= == != && || -*/ -class FlipBinaryOp: public CppQuickFixFactory +class FlipLogicalOperandsOp: public CppQuickFixOperation { public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) + FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority, + BinaryExpressionAST *binary, QString replacement) + : CppQuickFixOperation(interface) + , binary(binary) + , replacement(replacement) { - const QList<AST *> &path = interface->path(); - CppRefactoringFilePtr file = interface->currentFile(); + setPriority(priority); + } - int index = path.size() - 1; - BinaryExpressionAST *binary = path.at(index)->asBinaryExpression(); - if (! binary) - return; - if (! interface->isCursorOn(binary->binary_op_token)) - return; + QString description() const + { + if (replacement.isEmpty()) + return QApplication::translate("CppTools::QuickFix", "Swap Operands"); + else + return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement); + } - Kind flipToken; - switch (file->tokenAt(binary->binary_op_token).kind()) { - case T_LESS_EQUAL: - flipToken = T_GREATER_EQUAL; - break; - case T_LESS: - flipToken = T_GREATER; - break; - case T_GREATER: - flipToken = T_LESS; - break; - case T_GREATER_EQUAL: - flipToken = T_LESS_EQUAL; - break; - case T_EQUAL_EQUAL: - case T_EXCLAIM_EQUAL: - case T_AMPER_AMPER: - case T_PIPE_PIPE: - flipToken = T_EOF_SYMBOL; - break; - default: - return; - } + void perform() + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - QString replacement; - if (flipToken != T_EOF_SYMBOL) { - Token tok; - tok.f.kind = flipToken; - replacement = QLatin1String(tok.spell()); - } + ChangeSet changes; + changes.flip(currentFile->range(binary->left_expression), currentFile->range(binary->right_expression)); + if (! replacement.isEmpty()) + changes.replace(currentFile->range(binary->binary_op_token), replacement); - result.append(QuickFixOperation::Ptr(new Operation(interface, index, binary, replacement))); + currentFile->setChangeSet(changes); + currentFile->apply(); } private: - class Operation: public CppQuickFixOperation - { - public: - Operation(const CppQuickFixInterface &interface, - int priority, BinaryExpressionAST *binary, QString replacement) - : CppQuickFixOperation(interface) - , binary(binary) - , replacement(replacement) - { - setPriority(priority); - } - - virtual QString description() const - { - if (replacement.isEmpty()) - return QApplication::translate("CppTools::QuickFix", "Swap Operands"); - else - return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement); - } - - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + BinaryExpressionAST *binary; + QString replacement; +}; - ChangeSet changes; - changes.flip(currentFile->range(binary->left_expression), currentFile->range(binary->right_expression)); - if (! replacement.isEmpty()) - changes.replace(currentFile->range(binary->binary_op_token), replacement); +void FlipLogicalOperands::match(const CppQuickFixInterface &interface, QuickFixOperations &result) +{ + const QList<AST *> &path = interface->path(); + CppRefactoringFilePtr file = interface->currentFile(); - currentFile->setChangeSet(changes); - currentFile->apply(); - } + int index = path.size() - 1; + BinaryExpressionAST *binary = path.at(index)->asBinaryExpression(); + if (! binary) + return; + if (! interface->isCursorOn(binary->binary_op_token)) + return; - private: - BinaryExpressionAST *binary; - QString replacement; - }; -}; + Kind flipToken; + switch (file->tokenAt(binary->binary_op_token).kind()) { + case T_LESS_EQUAL: + flipToken = T_GREATER_EQUAL; + break; + case T_LESS: + flipToken = T_GREATER; + break; + case T_GREATER: + flipToken = T_LESS; + break; + case T_GREATER_EQUAL: + flipToken = T_LESS_EQUAL; + break; + case T_EQUAL_EQUAL: + case T_EXCLAIM_EQUAL: + case T_AMPER_AMPER: + case T_PIPE_PIPE: + flipToken = T_EOF_SYMBOL; + break; + default: + return; + } -/* - Rewrite - !a && !b + QString replacement; + if (flipToken != T_EOF_SYMBOL) { + Token tok; + tok.f.kind = flipToken; + replacement = QLatin1String(tok.spell()); + } - As - !(a || b) + result.append(QuickFixOperation::Ptr( + new FlipLogicalOperandsOp(interface, index, binary, replacement))); +} - Activates on: && -*/ -class RewriteLogicalAndOp: public CppQuickFixFactory +class RewriteLogicalAndOp: public CppQuickFixOperation { public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) + QSharedPointer<ASTPatternBuilder> mk; + UnaryExpressionAST *left; + UnaryExpressionAST *right; + BinaryExpressionAST *pattern; + + RewriteLogicalAndOp(const CppQuickFixInterface &interface) + : CppQuickFixOperation(interface) + , mk(new ASTPatternBuilder) { - BinaryExpressionAST *expression = 0; - const QList<AST *> &path = interface->path(); - CppRefactoringFilePtr file = interface->currentFile(); - - int index = path.size() - 1; - for (; index != -1; --index) { - expression = path.at(index)->asBinaryExpression(); - if (expression) - break; - } - - if (! expression) - return; + left = mk->UnaryExpression(); + right = mk->UnaryExpression(); + pattern = mk->BinaryExpression(left, right); + } - if (! interface->isCursorOn(expression->binary_op_token)) - return; + void perform() + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + + ChangeSet changes; + changes.replace(currentFile->range(pattern->binary_op_token), QLatin1String("||")); + changes.remove(currentFile->range(left->unary_op_token)); + changes.remove(currentFile->range(right->unary_op_token)); + const int start = currentFile->startOf(pattern); + const int end = currentFile->endOf(pattern); + changes.insert(start, QLatin1String("!(")); + changes.insert(end, QLatin1String(")")); + + currentFile->setChangeSet(changes); + currentFile->appendIndentRange(currentFile->range(pattern)); + currentFile->apply(); + } +}; - QSharedPointer<Operation> op(new Operation(interface)); +void RewriteLogicalAnd::match(const CppQuickFixInterface &interface, QuickFixOperations &result) +{ + BinaryExpressionAST *expression = 0; + const QList<AST *> &path = interface->path(); + CppRefactoringFilePtr file = interface->currentFile(); - if (expression->match(op->pattern, &matcher) && - file->tokenAt(op->pattern->binary_op_token).is(T_AMPER_AMPER) && - file->tokenAt(op->left->unary_op_token).is(T_EXCLAIM) && - file->tokenAt(op->right->unary_op_token).is(T_EXCLAIM)) { - op->setDescription(QApplication::translate("CppTools::QuickFix", "Rewrite Condition Using ||")); - op->setPriority(index); - result.append(op); - } + int index = path.size() - 1; + for (; index != -1; --index) { + expression = path.at(index)->asBinaryExpression(); + if (expression) + break; } -private: - class Operation: public CppQuickFixOperation - { - public: - QSharedPointer<ASTPatternBuilder> mk; - UnaryExpressionAST *left; - UnaryExpressionAST *right; - BinaryExpressionAST *pattern; - - Operation(const CppQuickFixInterface &interface) - : CppQuickFixOperation(interface) - , mk(new ASTPatternBuilder) - { - left = mk->UnaryExpression(); - right = mk->UnaryExpression(); - pattern = mk->BinaryExpression(left, right); - } + if (! expression) + return; - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + if (! interface->isCursorOn(expression->binary_op_token)) + return; - ChangeSet changes; - changes.replace(currentFile->range(pattern->binary_op_token), QLatin1String("||")); - changes.remove(currentFile->range(left->unary_op_token)); - changes.remove(currentFile->range(right->unary_op_token)); - const int start = currentFile->startOf(pattern); - const int end = currentFile->endOf(pattern); - changes.insert(start, QLatin1String("!(")); - changes.insert(end, QLatin1String(")")); + QSharedPointer<RewriteLogicalAndOp> op(new RewriteLogicalAndOp(interface)); - currentFile->setChangeSet(changes); - currentFile->appendIndentRange(currentFile->range(pattern)); - currentFile->apply(); - } - }; + if (expression->match(op->pattern, &matcher) && + file->tokenAt(op->pattern->binary_op_token).is(T_AMPER_AMPER) && + file->tokenAt(op->left->unary_op_token).is(T_EXCLAIM) && + file->tokenAt(op->right->unary_op_token).is(T_EXCLAIM)) { + op->setDescription(QApplication::translate("CppTools::QuickFix", "Rewrite Condition Using ||")); + op->setPriority(index); + result.append(op); + } +} -private: - ASTMatcher matcher; -}; +bool SplitSimpleDeclaration::checkDeclaration(SimpleDeclarationAST *declaration) +{ + if (! declaration->semicolon_token) + return false; -/* - Rewrite - int *a, b; + if (! declaration->decl_specifier_list) + return false; - As - int *a; - int b; + for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) { + SpecifierAST *specifier = it->value; - Activates on: the type or the variable names. -*/ -class SplitSimpleDeclarationOp: public CppQuickFixFactory -{ - static bool checkDeclaration(SimpleDeclarationAST *declaration) - { - if (! declaration->semicolon_token) + if (specifier->asEnumSpecifier() != 0) return false; - if (! declaration->decl_specifier_list) + else if (specifier->asClassSpecifier() != 0) return false; + } - for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) { - SpecifierAST *specifier = it->value; - - if (specifier->asEnumSpecifier() != 0) - return false; - - else if (specifier->asClassSpecifier() != 0) - return false; - } + if (! declaration->declarator_list) + return false; - if (! declaration->declarator_list) - return false; + else if (! declaration->declarator_list->next) + return false; - else if (! declaration->declarator_list->next) - return false; + return true; +} - return true; +class SplitSimpleDeclarationOp: public CppQuickFixOperation +{ +public: + SplitSimpleDeclarationOp(const CppQuickFixInterface &interface, int priority, + SimpleDeclarationAST *decl) + : CppQuickFixOperation(interface, priority) + , declaration(decl) + { + setDescription(QApplication::translate("CppTools::QuickFix", + "Split Declaration")); } -public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) + void perform() { - CoreDeclaratorAST *core_declarator = 0; - const QList<AST *> &path = interface->path(); - CppRefactoringFilePtr file = interface->currentFile(); - const int cursorPosition = file->cursor().selectionStart(); + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - for (int index = path.size() - 1; index != -1; --index) { - AST *node = path.at(index); + ChangeSet changes; - if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator()) - core_declarator = coreDecl; + SpecifierListAST *specifiers = declaration->decl_specifier_list; + int declSpecifiersStart = currentFile->startOf(specifiers->firstToken()); + int declSpecifiersEnd = currentFile->endOf(specifiers->lastToken() - 1); + int insertPos = currentFile->endOf(declaration->semicolon_token); - else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) { - if (checkDeclaration(simpleDecl)) { - SimpleDeclarationAST *declaration = simpleDecl; + DeclaratorAST *prevDeclarator = declaration->declarator_list->value; - const int startOfDeclSpecifier = file->startOf(declaration->decl_specifier_list->firstToken()); - const int endOfDeclSpecifier = file->endOf(declaration->decl_specifier_list->lastToken() - 1); + for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) { + DeclaratorAST *declarator = it->value; - if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) { - // the AST node under cursor is a specifier. - result.append(QuickFixOperation::Ptr(new Operation(interface, index, declaration))); - return; - } + changes.insert(insertPos, QLatin1String("\n")); + changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos); + changes.insert(insertPos, QLatin1String(" ")); + changes.move(currentFile->range(declarator), insertPos); + changes.insert(insertPos, QLatin1String(";")); - if (core_declarator && interface->isCursorOn(core_declarator)) { - // got a core-declarator under the text cursor. - result.append(QuickFixOperation::Ptr(new Operation(interface, index, declaration))); - return; - } - } + const int prevDeclEnd = currentFile->endOf(prevDeclarator); + changes.remove(prevDeclEnd, currentFile->startOf(declarator)); - return; - } + prevDeclarator = declarator; } + + currentFile->setChangeSet(changes); + currentFile->appendIndentRange(currentFile->range(declaration)); + currentFile->apply(); } private: - class Operation: public CppQuickFixOperation - { - public: - Operation(const CppQuickFixInterface &interface, int priority, SimpleDeclarationAST *decl) - : CppQuickFixOperation(interface, priority) - , declaration(decl) - { - setDescription(QApplication::translate("CppTools::QuickFix", - "Split Declaration")); - } - - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + SimpleDeclarationAST *declaration; +}; - ChangeSet changes; +void SplitSimpleDeclaration::match(const CppQuickFixInterface &interface, + QuickFixOperations &result) +{ + CoreDeclaratorAST *core_declarator = 0; + const QList<AST *> &path = interface->path(); + CppRefactoringFilePtr file = interface->currentFile(); + const int cursorPosition = file->cursor().selectionStart(); - SpecifierListAST *specifiers = declaration->decl_specifier_list; - int declSpecifiersStart = currentFile->startOf(specifiers->firstToken()); - int declSpecifiersEnd = currentFile->endOf(specifiers->lastToken() - 1); - int insertPos = currentFile->endOf(declaration->semicolon_token); + for (int index = path.size() - 1; index != -1; --index) { + AST *node = path.at(index); - DeclaratorAST *prevDeclarator = declaration->declarator_list->value; + if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator()) + core_declarator = coreDecl; - for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) { - DeclaratorAST *declarator = it->value; + else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) { + if (checkDeclaration(simpleDecl)) { + SimpleDeclarationAST *declaration = simpleDecl; - changes.insert(insertPos, QLatin1String("\n")); - changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos); - changes.insert(insertPos, QLatin1String(" ")); - changes.move(currentFile->range(declarator), insertPos); - changes.insert(insertPos, QLatin1String(";")); + const int startOfDeclSpecifier = file->startOf(declaration->decl_specifier_list->firstToken()); + const int endOfDeclSpecifier = file->endOf(declaration->decl_specifier_list->lastToken() - 1); - const int prevDeclEnd = currentFile->endOf(prevDeclarator); - changes.remove(prevDeclEnd, currentFile->startOf(declarator)); + if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) { + // the AST node under cursor is a specifier. + result.append(QuickFixOperation::Ptr( + new SplitSimpleDeclarationOp(interface, index, declaration))); + return; + } - prevDeclarator = declarator; + if (core_declarator && interface->isCursorOn(core_declarator)) { + // got a core-declarator under the text cursor. + result.append(QuickFixOperation::Ptr( + new SplitSimpleDeclarationOp(interface, index, declaration))); + return; + } } - currentFile->setChangeSet(changes); - currentFile->appendIndentRange(currentFile->range(declaration)); - currentFile->apply(); + return; } + } +} - private: - SimpleDeclarationAST *declaration; - }; -}; +class AddBracesToIfOp: public CppQuickFixOperation +{ +public: + AddBracesToIfOp(const CppQuickFixInterface &interface, int priority, StatementAST *statement) + : CppQuickFixOperation(interface, priority) + , _statement(statement) + { + setDescription(QApplication::translate("CppTools::QuickFix", + "Add Curly Braces")); + } -/* - Add curly braces to a if statement that doesn't already contain a - compound statement. I.e. + void perform() + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + + ChangeSet changes; + + const int start = currentFile->endOf(_statement->firstToken() - 1); + changes.insert(start, QLatin1String(" {")); + + const int end = currentFile->endOf(_statement->lastToken() - 1); + changes.insert(end, QLatin1String("\n}")); + + currentFile->setChangeSet(changes); + currentFile->appendIndentRange(Utils::ChangeSet::Range(start, end)); + currentFile->apply(); + } - if (a) - b; - becomes - if (a) - b; +private: + StatementAST *_statement; +}; - Activates on: the if -*/ -class AddBracesToIfOp: public CppQuickFixFactory +void AddBracesToIf::match(const CppQuickFixInterface &interface, QuickFixOperations &result) { -public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) - { - const QList<AST *> &path = interface->path(); + const QList<AST *> &path = interface->path(); - // show when we're on the 'if' of an if statement - int index = path.size() - 1; + // show when we're on the 'if' of an if statement + int index = path.size() - 1; + IfStatementAST *ifStatement = path.at(index)->asIfStatement(); + if (ifStatement && interface->isCursorOn(ifStatement->if_token) && ifStatement->statement + && ! ifStatement->statement->asCompoundStatement()) { + result.append(QuickFixOperation::Ptr( + new AddBracesToIfOp(interface, index, ifStatement->statement))); + return; + } + + // or if we're on the statement contained in the if + // ### This may not be such a good idea, consider nested ifs... + for (; index != -1; --index) { IfStatementAST *ifStatement = path.at(index)->asIfStatement(); - if (ifStatement && interface->isCursorOn(ifStatement->if_token) && ifStatement->statement + if (ifStatement && ifStatement->statement + && interface->isCursorOn(ifStatement->statement) && ! ifStatement->statement->asCompoundStatement()) { - result.append(QuickFixOperation::Ptr(new Operation(interface, index, ifStatement->statement))); + result.append(QuickFixOperation::Ptr( + new AddBracesToIfOp(interface, index, ifStatement->statement))); return; } + } - // or if we're on the statement contained in the if - // ### This may not be such a good idea, consider nested ifs... - for (; index != -1; --index) { - IfStatementAST *ifStatement = path.at(index)->asIfStatement(); - if (ifStatement && ifStatement->statement - && interface->isCursorOn(ifStatement->statement) - && ! ifStatement->statement->asCompoundStatement()) { - result.append(QuickFixOperation::Ptr(new Operation(interface, index, ifStatement->statement))); - return; - } - } + // ### This could very well be extended to the else branch + // and other nodes entirely. +} - // ### This could very well be extended to the else branch - // and other nodes entirely. +class MoveDeclarationOutOfIfOp: public CppQuickFixOperation +{ +public: + MoveDeclarationOutOfIfOp(const CppQuickFixInterface &interface) + : CppQuickFixOperation(interface) + { + setDescription(QApplication::translate("CppTools::QuickFix", + "Move Declaration out of Condition")); + + condition = mk.Condition(); + pattern = mk.IfStatement(condition); } -private: - class Operation: public CppQuickFixOperation + void perform() { - public: - Operation(const CppQuickFixInterface &interface, int priority, StatementAST *statement) - : CppQuickFixOperation(interface, priority) - , _statement(statement) - { - setDescription(QApplication::translate("CppTools::QuickFix", - "Add Curly Braces")); - } + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + ChangeSet changes; - ChangeSet changes; + changes.copy(currentFile->range(core), currentFile->startOf(condition)); - const int start = currentFile->endOf(_statement->firstToken() - 1); - changes.insert(start, QLatin1String(" {")); + int insertPos = currentFile->startOf(pattern); + changes.move(currentFile->range(condition), insertPos); + changes.insert(insertPos, QLatin1String(";\n")); - const int end = currentFile->endOf(_statement->lastToken() - 1); - changes.insert(end, QLatin1String("\n}")); - - currentFile->setChangeSet(changes); - currentFile->appendIndentRange(Utils::ChangeSet::Range(start, end)); - currentFile->apply(); - } + currentFile->setChangeSet(changes); + currentFile->appendIndentRange(currentFile->range(pattern)); + currentFile->apply(); + } - private: - StatementAST *_statement; - }; + ASTMatcher matcher; + ASTPatternBuilder mk; + CPPEditorWidget *editor; + ConditionAST *condition; + IfStatementAST *pattern; + CoreDeclaratorAST *core; }; -/* - Replace - if (Type name = foo()) {...} - - With - Type name = foo; - if (name) {...} - - Activates on: the name of the introduced variable -*/ -class MoveDeclarationOutOfIfOp: public CppQuickFixFactory +void MoveDeclarationOutOfIf::match(const CppQuickFixInterface &interface, + QuickFixOperations &result) { -public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) - { - const QList<AST *> &path = interface->path(); - QSharedPointer<Operation> op(new Operation(interface)); - - int index = path.size() - 1; - for (; index != -1; --index) { - if (IfStatementAST *statement = path.at(index)->asIfStatement()) { - if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) { - DeclaratorAST *declarator = op->condition->declarator; - op->core = declarator->core_declarator; - if (! op->core) - return; + const QList<AST *> &path = interface->path(); + typedef QSharedPointer<MoveDeclarationOutOfIfOp> Ptr; + Ptr op(new MoveDeclarationOutOfIfOp(interface)); + + int index = path.size() - 1; + for (; index != -1; --index) { + if (IfStatementAST *statement = path.at(index)->asIfStatement()) { + if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) { + DeclaratorAST *declarator = op->condition->declarator; + op->core = declarator->core_declarator; + if (! op->core) + return; - if (interface->isCursorOn(op->core)) { - op->setPriority(index); - result.append(op); - return; - } + if (interface->isCursorOn(op->core)) { + op->setPriority(index); + result.append(op); + return; } } } } +} -private: - class Operation: public CppQuickFixOperation +class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation +{ +public: + MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface) + : CppQuickFixOperation(interface) { - public: - Operation(const CppQuickFixInterface &interface) - : CppQuickFixOperation(interface) - { - setDescription(QApplication::translate("CppTools::QuickFix", - "Move Declaration out of Condition")); - - condition = mk.Condition(); - pattern = mk.IfStatement(condition); - } + setDescription(QApplication::translate("CppTools::QuickFix", + "Move Declaration out of Condition")); - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + condition = mk.Condition(); + pattern = mk.WhileStatement(condition); + } - ChangeSet changes; + void perform() + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - changes.copy(currentFile->range(core), currentFile->startOf(condition)); + ChangeSet changes; - int insertPos = currentFile->startOf(pattern); - changes.move(currentFile->range(condition), insertPos); - changes.insert(insertPos, QLatin1String(";\n")); + changes.insert(currentFile->startOf(condition), QLatin1String("(")); + changes.insert(currentFile->endOf(condition), QLatin1String(") != 0")); - currentFile->setChangeSet(changes); - currentFile->appendIndentRange(currentFile->range(pattern)); - currentFile->apply(); - } + int insertPos = currentFile->startOf(pattern); + const int conditionStart = currentFile->startOf(condition); + changes.move(conditionStart, currentFile->startOf(core), insertPos); + changes.copy(currentFile->range(core), insertPos); + changes.insert(insertPos, QLatin1String(";\n")); - ASTMatcher matcher; - ASTPatternBuilder mk; - CPPEditorWidget *editor; - ConditionAST *condition; - IfStatementAST *pattern; - CoreDeclaratorAST *core; - }; -}; - -/* - Replace - while (Type name = foo()) {...} + currentFile->setChangeSet(changes); + currentFile->appendIndentRange(currentFile->range(pattern)); + currentFile->apply(); + } - With - Type name; - while ((name = foo()) != 0) {...} + ASTMatcher matcher; + ASTPatternBuilder mk; + CPPEditorWidget *editor; + ConditionAST *condition; + WhileStatementAST *pattern; + CoreDeclaratorAST *core; +}; - Activates on: the name of the introduced variable -*/ -class MoveDeclarationOutOfWhileOp: public CppQuickFixFactory +void MoveDeclarationOutOfWhile::match(const CppQuickFixInterface &interface, + QuickFixOperations &result) { -public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) - { - const QList<AST *> &path = interface->path(); - QSharedPointer<Operation> op(new Operation(interface)); + const QList<AST *> &path = interface->path(); + QSharedPointer<MoveDeclarationOutOfWhileOp> op(new MoveDeclarationOutOfWhileOp(interface)); - int index = path.size() - 1; - for (; index != -1; --index) { - if (WhileStatementAST *statement = path.at(index)->asWhileStatement()) { - if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) { - DeclaratorAST *declarator = op->condition->declarator; - op->core = declarator->core_declarator; + int index = path.size() - 1; + for (; index != -1; --index) { + if (WhileStatementAST *statement = path.at(index)->asWhileStatement()) { + if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) { + DeclaratorAST *declarator = op->condition->declarator; + op->core = declarator->core_declarator; - if (! op->core) - return; + if (! op->core) + return; - if (! declarator->equal_token) - return; + if (! declarator->equal_token) + return; - if (! declarator->initializer) - return; + if (! declarator->initializer) + return; - if (interface->isCursorOn(op->core)) { - op->setPriority(index); - result.append(op); - return; - } + if (interface->isCursorOn(op->core)) { + op->setPriority(index); + result.append(op); + return; } } } } +} -private: - class Operation: public CppQuickFixOperation +class SplitIfStatementOp: public CppQuickFixOperation +{ +public: + SplitIfStatementOp(const CppQuickFixInterface &interface, int priority, + IfStatementAST *pattern, BinaryExpressionAST *condition) + : CppQuickFixOperation(interface, priority) + , pattern(pattern) + , condition(condition) { - public: - Operation(const CppQuickFixInterface &interface) - : CppQuickFixOperation(interface) - { - setDescription(QApplication::translate("CppTools::QuickFix", - "Move Declaration out of Condition")); - - condition = mk.Condition(); - pattern = mk.WhileStatement(condition); - } - - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - - ChangeSet changes; - - changes.insert(currentFile->startOf(condition), QLatin1String("(")); - changes.insert(currentFile->endOf(condition), QLatin1String(") != 0")); + setDescription(QApplication::translate("CppTools::QuickFix", + "Split if Statement")); + } - int insertPos = currentFile->startOf(pattern); - const int conditionStart = currentFile->startOf(condition); - changes.move(conditionStart, currentFile->startOf(core), insertPos); - changes.copy(currentFile->range(core), insertPos); - changes.insert(insertPos, QLatin1String(";\n")); + void perform() + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - currentFile->setChangeSet(changes); - currentFile->appendIndentRange(currentFile->range(pattern)); - currentFile->apply(); - } + const Token binaryToken = currentFile->tokenAt(condition->binary_op_token); - ASTMatcher matcher; - ASTPatternBuilder mk; - CPPEditorWidget *editor; - ConditionAST *condition; - WhileStatementAST *pattern; - CoreDeclaratorAST *core; - }; -}; + if (binaryToken.is(T_AMPER_AMPER)) + splitAndCondition(currentFile); + else + splitOrCondition(currentFile); + } -/* - Replace - if (something && something_else) { - } - - with - if (something) - if (something_else) - } - - and - if (something || something_else) - x; - - with - if (something) - x; - else if (something_else) - x; - - Activates on: && or || -*/ -class SplitIfStatementOp: public CppQuickFixFactory -{ -public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) + void splitAndCondition(CppRefactoringFilePtr currentFile) const { - IfStatementAST *pattern = 0; - const QList<AST *> &path = interface->path(); - - int index = path.size() - 1; - for (; index != -1; --index) { - AST *node = path.at(index); - if (IfStatementAST *stmt = node->asIfStatement()) { - pattern = stmt; - break; - } - } + ChangeSet changes; - if (! pattern || ! pattern->statement) - return; + int startPos = currentFile->startOf(pattern); + changes.insert(startPos, QLatin1String("if (")); + changes.move(currentFile->range(condition->left_expression), startPos); + changes.insert(startPos, QLatin1String(") {\n")); - unsigned splitKind = 0; - for (++index; index < path.size(); ++index) { - AST *node = path.at(index); - BinaryExpressionAST *condition = node->asBinaryExpression(); - if (! condition) - return; - - Token binaryToken = interface->currentFile()->tokenAt(condition->binary_op_token); - - // only accept a chain of ||s or &&s - no mixing - if (! splitKind) { - splitKind = binaryToken.kind(); - if (splitKind != T_AMPER_AMPER && splitKind != T_PIPE_PIPE) - return; - // we can't reliably split &&s in ifs with an else branch - if (splitKind == T_AMPER_AMPER && pattern->else_statement) - return; - } else if (splitKind != binaryToken.kind()) { - return; - } + const int lExprEnd = currentFile->endOf(condition->left_expression); + changes.remove(lExprEnd, currentFile->startOf(condition->right_expression)); + changes.insert(currentFile->endOf(pattern), QLatin1String("\n}")); - if (interface->isCursorOn(condition->binary_op_token)) { - result.append(QuickFixOperation::Ptr(new Operation(interface, index, pattern, condition))); - return; - } - } + currentFile->setChangeSet(changes); + currentFile->appendIndentRange(currentFile->range(pattern)); + currentFile->apply(); } -private: - class Operation: public CppQuickFixOperation + void splitOrCondition(CppRefactoringFilePtr currentFile) const { - public: - Operation(const CppQuickFixInterface &interface, int priority, - IfStatementAST *pattern, BinaryExpressionAST *condition) - : CppQuickFixOperation(interface, priority) - , pattern(pattern) - , condition(condition) - { - setDescription(QApplication::translate("CppTools::QuickFix", - "Split if Statement")); - } + ChangeSet changes; - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + StatementAST *ifTrueStatement = pattern->statement; + CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement(); - const Token binaryToken = currentFile->tokenAt(condition->binary_op_token); + int insertPos = currentFile->endOf(ifTrueStatement); + if (compoundStatement) + changes.insert(insertPos, QLatin1String(" ")); + else + changes.insert(insertPos, QLatin1String("\n")); + changes.insert(insertPos, QLatin1String("else if (")); - if (binaryToken.is(T_AMPER_AMPER)) - splitAndCondition(currentFile); - else - splitOrCondition(currentFile); - } + const int rExprStart = currentFile->startOf(condition->right_expression); + changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos); + changes.insert(insertPos, QLatin1String(")")); - void splitAndCondition(CppRefactoringFilePtr currentFile) - { - ChangeSet changes; + const int rParenEnd = currentFile->endOf(pattern->rparen_token); + changes.copy(rParenEnd, currentFile->endOf(pattern->statement), insertPos); - int startPos = currentFile->startOf(pattern); - changes.insert(startPos, QLatin1String("if (")); - changes.move(currentFile->range(condition->left_expression), startPos); - changes.insert(startPos, QLatin1String(") {\n")); + const int lExprEnd = currentFile->endOf(condition->left_expression); + changes.remove(lExprEnd, currentFile->startOf(condition->right_expression)); - const int lExprEnd = currentFile->endOf(condition->left_expression); - changes.remove(lExprEnd, currentFile->startOf(condition->right_expression)); - changes.insert(currentFile->endOf(pattern), QLatin1String("\n}")); - - currentFile->setChangeSet(changes); - currentFile->appendIndentRange(currentFile->range(pattern)); - currentFile->apply(); - } + currentFile->setChangeSet(changes); + currentFile->appendIndentRange(currentFile->range(pattern)); + currentFile->apply(); + } - void splitOrCondition(CppRefactoringFilePtr currentFile) - { - ChangeSet changes; +private: + IfStatementAST *pattern; + BinaryExpressionAST *condition; +}; - StatementAST *ifTrueStatement = pattern->statement; - CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement(); +void SplitIfStatement::match(const CppQuickFixInterface &interface, QuickFixOperations &result) +{ + IfStatementAST *pattern = 0; + const QList<AST *> &path = interface->path(); - int insertPos = currentFile->endOf(ifTrueStatement); - if (compoundStatement) - changes.insert(insertPos, QLatin1String(" ")); - else - changes.insert(insertPos, QLatin1String("\n")); - changes.insert(insertPos, QLatin1String("else if (")); + int index = path.size() - 1; + for (; index != -1; --index) { + AST *node = path.at(index); + if (IfStatementAST *stmt = node->asIfStatement()) { + pattern = stmt; + break; + } + } - const int rExprStart = currentFile->startOf(condition->right_expression); - changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos); - changes.insert(insertPos, QLatin1String(")")); + if (! pattern || ! pattern->statement) + return; - const int rParenEnd = currentFile->endOf(pattern->rparen_token); - changes.copy(rParenEnd, currentFile->endOf(pattern->statement), insertPos); + unsigned splitKind = 0; + for (++index; index < path.size(); ++index) { + AST *node = path.at(index); + BinaryExpressionAST *condition = node->asBinaryExpression(); + if (! condition) + return; - const int lExprEnd = currentFile->endOf(condition->left_expression); - changes.remove(lExprEnd, currentFile->startOf(condition->right_expression)); + Token binaryToken = interface->currentFile()->tokenAt(condition->binary_op_token); - currentFile->setChangeSet(changes); - currentFile->appendIndentRange(currentFile->range(pattern)); - currentFile->apply(); + // only accept a chain of ||s or &&s - no mixing + if (! splitKind) { + splitKind = binaryToken.kind(); + if (splitKind != T_AMPER_AMPER && splitKind != T_PIPE_PIPE) + return; + // we can't reliably split &&s in ifs with an else branch + if (splitKind == T_AMPER_AMPER && pattern->else_statement) + return; + } else if (splitKind != binaryToken.kind()) { + return; } - private: - IfStatementAST *pattern; - BinaryExpressionAST *condition; - }; -}; - -/* - Replace - "abcd" -> QLatin1String("abcd") - @"abcd" -> QLatin1String("abcd") (Objective C) - 'a' -> QLatin1Char('a') - 'a' -> "a" - "a" -> 'a' or QLatin1Char('a') (Single character string constants) - "\n" -> '\n', QLatin1Char('\n') - Except if they are already enclosed in - QLatin1Char, QT_TRANSLATE_NOOP, tr, - trUtf8, QLatin1Literal, QLatin1String - - Activates on: the string or character literal -*/ + if (interface->isCursorOn(condition->binary_op_token)) { + result.append(QuickFixOperation::Ptr( + new SplitIfStatementOp(interface, index, pattern, condition))); + return; + } + } +} static inline QString msgQtStringLiteralDescription(const QString &replacement, int qtVersion) { @@ -935,56 +821,9 @@ static inline QString msgQtStringLiteralDescription(const QString &replacement) return QApplication::translate("CppTools::QuickFix", "Enclose in %1(...)").arg(replacement); } -class WrapStringLiteral: public CppQuickFixFactory -{ -public: - typedef const CppQuickFixInterface AssistInterfacePtr; - - enum ActionFlags - { - EncloseInQLatin1CharAction = 0x1, EncloseInQLatin1StringAction = 0x2, EncloseInQStringLiteralAction = 0x4, - EncloseActionMask = EncloseInQLatin1CharAction | EncloseInQLatin1StringAction | EncloseInQStringLiteralAction, - TranslateTrAction = 0x8, TranslateQCoreApplicationAction = 0x10, TranslateNoopAction = 0x20, - TranslationMask = TranslateTrAction | TranslateQCoreApplicationAction | TranslateNoopAction, - RemoveObjectiveCAction = 0x40, - ConvertEscapeSequencesToCharAction = 0x100, ConvertEscapeSequencesToStringAction = 0x200, - SingleQuoteAction = 0x400, DoubleQuoteAction = 0x800 - }; - - enum Type { TypeString, TypeObjCString, TypeChar, TypeNone }; - - //void match(const AssistInterfacePtr &interface, QuickFixOperations &result); - void match(const CppQuickFixInterface &interface, QuickFixOperations &result); - static QString replacement(unsigned actions); - static QByteArray stringToCharEscapeSequences(const QByteArray &content); - static QByteArray charToStringEscapeSequences(const QByteArray &content); - - static ExpressionAST *analyze(const QList<AST *> &path, const CppRefactoringFilePtr &file, - Type *type, - QByteArray *enclosingFunction = 0, - CallAST **enclosingFunctionCall = 0); - - // Operations performs the operations of type ActionFlags passed in as actions. - class Operation : public CppQuickFixOperation - { - public: - Operation(const AssistInterfacePtr &interface, int priority, - unsigned actions, const QString &description, ExpressionAST *literal, - const QString &translationContext = QString()); - - void perform(); - - private: - const unsigned m_actions; - ExpressionAST *m_literal; - const QString m_translationContext; - }; -}; - /* Analze a string/character literal like "x", QLatin1String("x") and return the literal * (StringLiteral or NumericLiteral for characters) and its type * and the enclosing function (QLatin1String, tr...) */ - ExpressionAST *WrapStringLiteral::analyze(const QList<AST *> &path, const CppRefactoringFilePtr &file, Type *type, @@ -1029,6 +868,87 @@ ExpressionAST *WrapStringLiteral::analyze(const QList<AST *> &path, return literal; } +/// Operation performs the operations of type ActionFlags passed in as actions. +class WrapStringLiteralOp : public CppQuickFixOperation +{ +public: + typedef WrapStringLiteral Factory; + + WrapStringLiteralOp(const CppQuickFixInterface &interface, int priority, + unsigned actions, const QString &description, ExpressionAST *literal, + const QString &translationContext = QString()) + : CppQuickFixOperation(interface, priority), m_actions(actions), m_literal(literal), + m_translationContext(translationContext) + { + setDescription(description); + } + + void perform() + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + + ChangeSet changes; + + const int startPos = currentFile->startOf(m_literal); + const int endPos = currentFile->endOf(m_literal); + + // kill leading '@'. No need to adapt endPos, that is done by ChangeSet + if (m_actions & Factory::RemoveObjectiveCAction) + changes.remove(startPos, startPos + 1); + + // Fix quotes + if (m_actions & (Factory::SingleQuoteAction | Factory::DoubleQuoteAction)) { + const QString newQuote((m_actions & Factory::SingleQuoteAction) ? QLatin1Char('\'') : QLatin1Char('"')); + changes.replace(startPos, startPos + 1, newQuote); + changes.replace(endPos - 1, endPos, newQuote); + } + + // Convert single character strings into character constants + if (m_actions & Factory::ConvertEscapeSequencesToCharAction) { + StringLiteralAST *stringLiteral = m_literal->asStringLiteral(); + QTC_ASSERT(stringLiteral, return ;); + const QByteArray oldContents(currentFile->tokenAt(stringLiteral->literal_token).identifier->chars()); + const QByteArray newContents = Factory::stringToCharEscapeSequences(oldContents); + QTC_ASSERT(!newContents.isEmpty(), return ;); + if (oldContents != newContents) + changes.replace(startPos + 1, endPos -1, QString::fromLatin1(newContents)); + } + + // Convert character constants into strings constants + if (m_actions & Factory::ConvertEscapeSequencesToStringAction) { + NumericLiteralAST *charLiteral = m_literal->asNumericLiteral(); // char 'c' constants are numerical. + QTC_ASSERT(charLiteral, return ;); + const QByteArray oldContents(currentFile->tokenAt(charLiteral->literal_token).identifier->chars()); + const QByteArray newContents = Factory::charToStringEscapeSequences(oldContents); + QTC_ASSERT(!newContents.isEmpty(), return ;); + if (oldContents != newContents) + changes.replace(startPos + 1, endPos -1, QString::fromLatin1(newContents)); + } + + // Enclose in literal or translation function, macro. + if (m_actions & (Factory::EncloseActionMask | Factory::TranslationMask)) { + changes.insert(endPos, QString(QLatin1Char(')'))); + QString leading = Factory::replacement(m_actions); + leading += QLatin1Char('('); + if (m_actions & (Factory::TranslateQCoreApplicationAction | Factory::TranslateNoopAction)) { + leading += QLatin1Char('"'); + leading += m_translationContext; + leading += QLatin1String("\", "); + } + changes.insert(startPos, leading); + } + + currentFile->setChangeSet(changes); + currentFile->apply(); + } + +private: + const unsigned m_actions; + ExpressionAST *m_literal; + const QString m_translationContext; +}; + void WrapStringLiteral::match(const CppQuickFixInterface &interface, QuickFixOperations &result) { typedef CppQuickFixOperation::Ptr OperationPtr; @@ -1037,7 +957,7 @@ void WrapStringLiteral::match(const CppQuickFixInterface &interface, QuickFixOpe QByteArray enclosingFunction; const QList<AST *> &path = interface->path(); CppRefactoringFilePtr file = interface->currentFile(); - ExpressionAST *literal = WrapStringLiteral::analyze(path, file, &type, &enclosingFunction); + ExpressionAST *literal = analyze(path, file, &type, &enclosingFunction); if (!literal || type == TypeNone) return; if ((type == TypeChar && enclosingFunction == "QLatin1Char") @@ -1048,17 +968,17 @@ void WrapStringLiteral::match(const CppQuickFixInterface &interface, QuickFixOpe const int priority = path.size() - 1; // very high priority if (type == TypeChar) { unsigned actions = EncloseInQLatin1CharAction; - QString description = msgQtStringLiteralDescription(WrapStringLiteral::replacement(actions)); - result << OperationPtr(new Operation(interface, priority, actions, - description, literal)); + QString description = msgQtStringLiteralDescription(replacement(actions)); + result << OperationPtr(new WrapStringLiteralOp(interface, priority, actions, + description, literal)); if (NumericLiteralAST *charLiteral = literal->asNumericLiteral()) { const QByteArray contents(file->tokenAt(charLiteral->literal_token).identifier->chars()); if (!charToStringEscapeSequences(contents).isEmpty()) { actions = DoubleQuoteAction | ConvertEscapeSequencesToStringAction; description = QApplication::translate("CppTools::QuickFix", "Convert to String Literal"); - result << OperationPtr(new Operation(interface, priority, actions, - description, literal)); + result << OperationPtr(new WrapStringLiteralOp(interface, priority, actions, + description, literal)); } } } else { @@ -1072,23 +992,24 @@ void WrapStringLiteral::match(const CppQuickFixInterface &interface, QuickFixOpe | ConvertEscapeSequencesToCharAction | objectiveCActions; QString description = QApplication::translate("CppTools::QuickFix", "Convert to Character Literal and Enclose in QLatin1Char(...)"); - result << OperationPtr(new Operation(interface, priority, - actions, description, literal)); + result << OperationPtr(new WrapStringLiteralOp(interface, priority, + actions, description, literal)); actions &= ~EncloseInQLatin1CharAction; description = QApplication::translate("CppTools::QuickFix", "Convert to Character Literal"); - result << OperationPtr(new Operation(interface, priority, - actions, description, literal)); + result << OperationPtr(new WrapStringLiteralOp(interface, priority, + actions, description, literal)); } } actions = EncloseInQLatin1StringAction | objectiveCActions; - result << OperationPtr(new Operation(interface, priority, actions, - msgQtStringLiteralDescription(WrapStringLiteral::replacement(actions), 4), - literal)); + result << OperationPtr( + new WrapStringLiteralOp(interface, priority, actions, + msgQtStringLiteralDescription(replacement(actions), 4), + literal)); actions = EncloseInQStringLiteralAction | objectiveCActions; - result << OperationPtr(new Operation(interface, priority, actions, - msgQtStringLiteralDescription(WrapStringLiteral::replacement(actions), 5), - literal)); + result << OperationPtr( + new WrapStringLiteralOp(interface, priority, actions, + msgQtStringLiteralDescription(replacement(actions), 5), literal)); } } @@ -1131,394 +1052,331 @@ QByteArray WrapStringLiteral::charToStringEscapeSequences(const QByteArray &cont return QByteArray(); } -WrapStringLiteral::Operation::Operation(const AssistInterfacePtr &interface, int priority, - unsigned actions, const QString &description, ExpressionAST *literal, - const QString &translationContext) - : CppQuickFixOperation(interface, priority), m_actions(actions), m_literal(literal), - m_translationContext(translationContext) +void TranslateStringLiteral::match(const CppQuickFixInterface &interface, + QuickFixOperations &result) { - setDescription(description); -} + // Initialize + WrapStringLiteral::Type type = WrapStringLiteral::TypeNone; + QByteArray enclosingFunction; + const QList<AST *> &path = interface->path(); + CppRefactoringFilePtr file = interface->currentFile(); + ExpressionAST *literal = WrapStringLiteral::analyze(path, file, &type, &enclosingFunction); + if (!literal || type != WrapStringLiteral::TypeString + || isQtStringLiteral(enclosingFunction) || isQtStringTranslation(enclosingFunction)) + return; -void WrapStringLiteral::Operation::perform() -{ - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - - ChangeSet changes; - - const int startPos = currentFile->startOf(m_literal); - const int endPos = currentFile->endOf(m_literal); - - // kill leading '@'. No need to adapt endPos, that is done by ChangeSet - if (m_actions & RemoveObjectiveCAction) - changes.remove(startPos, startPos + 1); - - // Fix quotes - if (m_actions & (SingleQuoteAction | DoubleQuoteAction)) { - const QString newQuote((m_actions & SingleQuoteAction) ? QLatin1Char('\'') : QLatin1Char('"')); - changes.replace(startPos, startPos + 1, newQuote); - changes.replace(endPos - 1, endPos, newQuote); - } - - // Convert single character strings into character constants - if (m_actions & ConvertEscapeSequencesToCharAction) { - StringLiteralAST *stringLiteral = m_literal->asStringLiteral(); - QTC_ASSERT(stringLiteral, return ;); - const QByteArray oldContents(currentFile->tokenAt(stringLiteral->literal_token).identifier->chars()); - const QByteArray newContents = stringToCharEscapeSequences(oldContents); - QTC_ASSERT(!newContents.isEmpty(), return ;); - if (oldContents != newContents) - changes.replace(startPos + 1, endPos -1, QString::fromLatin1(newContents)); - } - - // Convert character constants into strings constants - if (m_actions & ConvertEscapeSequencesToStringAction) { - NumericLiteralAST *charLiteral = m_literal->asNumericLiteral(); // char 'c' constants are numerical. - QTC_ASSERT(charLiteral, return ;); - const QByteArray oldContents(currentFile->tokenAt(charLiteral->literal_token).identifier->chars()); - const QByteArray newContents = charToStringEscapeSequences(oldContents); - QTC_ASSERT(!newContents.isEmpty(), return ;); - if (oldContents != newContents) - changes.replace(startPos + 1, endPos -1, QString::fromLatin1(newContents)); - } - - // Enclose in literal or translation function, macro. - if (m_actions & (EncloseActionMask | TranslationMask)) { - changes.insert(endPos, QString(QLatin1Char(')'))); - QString leading = WrapStringLiteral::replacement(m_actions); - leading += QLatin1Char('('); - if (m_actions & (TranslateQCoreApplicationAction | TranslateNoopAction)) { - leading += QLatin1Char('"'); - leading += m_translationContext; - leading += QLatin1String("\", "); + QString trContext; + + QSharedPointer<Control> control = interface->context().control(); + const Name *trName = control->identifier("tr"); + + // Check whether we are in a method: + const QString description = QApplication::translate("CppTools::QuickFix", "Mark as Translatable"); + for (int i = path.size() - 1; i >= 0; --i) { + if (FunctionDefinitionAST *definition = path.at(i)->asFunctionDefinition()) { + Function *function = definition->symbol; + ClassOrNamespace *b = interface->context().lookupType(function); + if (b) { + // Do we have a tr method? + foreach (const LookupItem &r, b->find(trName)) { + Symbol *s = r.declaration(); + if (s->type()->isFunctionType()) { + // no context required for tr + result.append(QuickFixOperation::Ptr( + new WrapStringLiteralOp(interface, path.size() - 1, + WrapStringLiteral::TranslateTrAction, + description, literal))); + return; + } + } + } + // We need to do a QCA::translate, so we need a context. + // Use fully qualified class name: + Overview oo; + foreach (const Name *n, LookupContext::path(function)) { + if (!trContext.isEmpty()) + trContext.append(QLatin1String("::")); + trContext.append(oo.prettyName(n)); + } + // ... or global if none available! + if (trContext.isEmpty()) + trContext = QLatin1String("GLOBAL"); + result.append(QuickFixOperation::Ptr( + new WrapStringLiteralOp(interface, path.size() - 1, + WrapStringLiteral::TranslateQCoreApplicationAction, + description, literal, trContext))); + return; } - changes.insert(startPos, leading); } - currentFile->setChangeSet(changes); - currentFile->apply(); + // We need to use Q_TRANSLATE_NOOP + result.append(QuickFixOperation::Ptr( + new WrapStringLiteralOp(interface, path.size() - 1, + WrapStringLiteral::TranslateNoopAction, + description, literal, trContext))); } -/* - Replace - "abcd" - With - tr("abcd") or - QCoreApplication::translate("CONTEXT", "abcd") or - QT_TRANSLATE_NOOP("GLOBAL", "abcd") - depending on what is available. - - Activates on: the string literal -*/ -class TranslateStringLiteral: public CppQuickFixFactory +class ConvertCStringToNSStringOp: public CppQuickFixOperation { public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) + ConvertCStringToNSStringOp(const CppQuickFixInterface &interface, int priority, + StringLiteralAST *stringLiteral, CallAST *qlatin1Call) + : CppQuickFixOperation(interface, priority) + , stringLiteral(stringLiteral) + , qlatin1Call(qlatin1Call) { - // Initialize - WrapStringLiteral::Type type = WrapStringLiteral::TypeNone; - QByteArray enclosingFunction; - const QList<AST *> &path = interface->path(); - CppRefactoringFilePtr file = interface->currentFile(); - ExpressionAST *literal = WrapStringLiteral::analyze(path, file, &type, &enclosingFunction); - if (!literal || type != WrapStringLiteral::TypeString - || isQtStringLiteral(enclosingFunction) || isQtStringTranslation(enclosingFunction)) - return; - - QString trContext; - - QSharedPointer<Control> control = interface->context().control(); - const Name *trName = control->identifier("tr"); - - // Check whether we are in a method: - const QString description = QApplication::translate("CppTools::QuickFix", "Mark as Translatable"); - for (int i = path.size() - 1; i >= 0; --i) { - if (FunctionDefinitionAST *definition = path.at(i)->asFunctionDefinition()) { - Function *function = definition->symbol; - ClassOrNamespace *b = interface->context().lookupType(function); - if (b) { - // Do we have a tr method? - foreach (const LookupItem &r, b->find(trName)) { - Symbol *s = r.declaration(); - if (s->type()->isFunctionType()) { - // no context required for tr - result.append(QuickFixOperation::Ptr(new WrapStringLiteral::Operation(interface, path.size() - 1, - WrapStringLiteral::TranslateTrAction, - description, literal))); - return; - } - } - } - // We need to do a QCA::translate, so we need a context. - // Use fully qualified class name: - Overview oo; - foreach (const Name *n, LookupContext::path(function)) { - if (!trContext.isEmpty()) - trContext.append(QLatin1String("::")); - trContext.append(oo.prettyName(n)); - } - // ... or global if none available! - if (trContext.isEmpty()) - trContext = QLatin1String("GLOBAL"); - result.append(QuickFixOperation::Ptr(new WrapStringLiteral::Operation(interface, path.size() - 1, - WrapStringLiteral::TranslateQCoreApplicationAction, - description, literal, trContext))); - return; - } - } - - // We need to use Q_TRANSLATE_NOOP - result.append(QuickFixOperation::Ptr(new WrapStringLiteral::Operation(interface, path.size() - 1, - WrapStringLiteral::TranslateNoopAction, - description, literal, trContext))); + setDescription(QApplication::translate("CppTools::QuickFix", + "Convert to Objective-C String Literal")); } -}; -/** - * Replace - * "abcd" - * QLatin1String("abcd") - * QLatin1Literal("abcd") - * With - * @"abcd" - * - * Activates on: the string literal, if the file type is a Objective-C(++) file. - */ -class CStringToNSString: public CppQuickFixFactory -{ -public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) + void perform() { - CppRefactoringFilePtr file = interface->currentFile(); + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - if (interface->editor()->mimeType() != QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)) - return; + ChangeSet changes; - WrapStringLiteral::Type type = WrapStringLiteral::TypeNone; - QByteArray enclosingFunction; - CallAST *qlatin1Call; - const QList<AST *> &path = interface->path(); - ExpressionAST *literal = WrapStringLiteral::analyze(path, file, &type, &enclosingFunction, &qlatin1Call); - if (!literal || type != WrapStringLiteral::TypeString) - return; - if (!isQtStringLiteral(enclosingFunction)) - qlatin1Call = 0; + if (qlatin1Call) { + changes.replace(currentFile->startOf(qlatin1Call), currentFile->startOf(stringLiteral), QLatin1String("@")); + changes.remove(currentFile->endOf(stringLiteral), currentFile->endOf(qlatin1Call)); + } else { + changes.insert(currentFile->startOf(stringLiteral), QLatin1String("@")); + } - result.append(QuickFixOperation::Ptr(new Operation(interface, path.size() - 1, literal->asStringLiteral(), qlatin1Call))); + currentFile->setChangeSet(changes); + currentFile->apply(); } private: - class Operation: public CppQuickFixOperation - { - public: - Operation(const CppQuickFixInterface &interface, int priority, StringLiteralAST *stringLiteral, CallAST *qlatin1Call) - : CppQuickFixOperation(interface, priority) - , stringLiteral(stringLiteral) - , qlatin1Call(qlatin1Call) - { - setDescription(QApplication::translate("CppTools::QuickFix", - "Convert to Objective-C String Literal")); - } - - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + StringLiteralAST *stringLiteral; + CallAST *qlatin1Call; +}; - ChangeSet changes; +void ConvertCStringToNSString::match(const CppQuickFixInterface &interface, + QuickFixOperations &result) +{ + CppRefactoringFilePtr file = interface->currentFile(); - if (qlatin1Call) { - changes.replace(currentFile->startOf(qlatin1Call), currentFile->startOf(stringLiteral), QLatin1String("@")); - changes.remove(currentFile->endOf(stringLiteral), currentFile->endOf(qlatin1Call)); - } else { - changes.insert(currentFile->startOf(stringLiteral), QLatin1String("@")); - } + if (interface->editor()->mimeType() != QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)) + return; - currentFile->setChangeSet(changes); - currentFile->apply(); - } + WrapStringLiteral::Type type = WrapStringLiteral::TypeNone; + QByteArray enclosingFunction; + CallAST *qlatin1Call; + const QList<AST *> &path = interface->path(); + ExpressionAST *literal = WrapStringLiteral::analyze(path, file, &type, &enclosingFunction, &qlatin1Call); + if (!literal || type != WrapStringLiteral::TypeString) + return; + if (!isQtStringLiteral(enclosingFunction)) + qlatin1Call = 0; - private: - StringLiteralAST *stringLiteral; - CallAST *qlatin1Call; - }; -}; + result.append(QuickFixOperation::Ptr( + new ConvertCStringToNSStringOp(interface, path.size() - 1, literal->asStringLiteral(), + qlatin1Call))); +} -/* - Base class for converting numeric literals between decimal, octal and hex. - Does the base check for the specific ones and parses the number. - Test cases: - 0xFA0Bu; - 0X856A; - 298.3; - 199; - 074; - 199L; - 074L; - -199; - -017; - 0783; // invalid octal - 0; // border case, allow only hex<->decimal - - Activates on: numeric literals -*/ -class ConvertNumericLiteral: public CppQuickFixFactory +class ConvertNumericLiteralOp: public CppQuickFixOperation { public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) + ConvertNumericLiteralOp(const CppQuickFixInterface &interface, int start, int end, + const QString &replacement) + : CppQuickFixOperation(interface) + , start(start) + , end(end) + , replacement(replacement) + {} + + void perform() { - const QList<AST *> &path = interface->path(); - CppRefactoringFilePtr file = interface->currentFile(); + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - if (path.isEmpty()) - return; + ChangeSet changes; + changes.replace(start, end, replacement); + currentFile->setChangeSet(changes); + currentFile->apply(); + } - NumericLiteralAST *literal = path.last()->asNumericLiteral(); +private: + int start, end; + QString replacement; +}; - if (! literal) - return; +void ConvertNumericLiteral::match(const CppQuickFixInterface &interface, QuickFixOperations &result) +{ + const QList<AST *> &path = interface->path(); + CppRefactoringFilePtr file = interface->currentFile(); - Token token = file->tokenAt(literal->asNumericLiteral()->literal_token); - if (!token.is(T_NUMERIC_LITERAL)) - return; - const NumericLiteral *numeric = token.number; - if (numeric->isDouble() || numeric->isFloat()) - return; + if (path.isEmpty()) + return; - // remove trailing L or U and stuff - const char * const spell = numeric->chars(); - int numberLength = numeric->size(); - while (numberLength > 0 && !std::isxdigit(spell[numberLength - 1])) - --numberLength; - if (numberLength < 1) - return; + NumericLiteralAST *literal = path.last()->asNumericLiteral(); - // convert to number - bool valid; - ulong value = QString::fromUtf8(spell).left(numberLength).toULong(&valid, 0); - if (!valid) // e.g. octal with digit > 7 - return; + if (! literal) + return; - const int priority = path.size() - 1; // very high priority - const int start = file->startOf(literal); - const char * const str = numeric->chars(); + Token token = file->tokenAt(literal->asNumericLiteral()->literal_token); + if (!token.is(T_NUMERIC_LITERAL)) + return; + const NumericLiteral *numeric = token.number; + if (numeric->isDouble() || numeric->isFloat()) + return; - if (!numeric->isHex()) { + // remove trailing L or U and stuff + const char * const spell = numeric->chars(); + int numberLength = numeric->size(); + while (numberLength > 0 && !std::isxdigit(spell[numberLength - 1])) + --numberLength; + if (numberLength < 1) + return; + + // convert to number + bool valid; + ulong value = QString::fromUtf8(spell).left(numberLength).toULong(&valid, 0); + if (!valid) // e.g. octal with digit > 7 + return; + + const int priority = path.size() - 1; // very high priority + const int start = file->startOf(literal); + const char * const str = numeric->chars(); + + if (!numeric->isHex()) { + /* + Convert integer literal to hex representation. + Replace + 32 + 040 + With + 0x20 + + */ + QString replacement; + replacement.sprintf("0x%lX", value); + QuickFixOperation::Ptr op( + new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement)); + op->setDescription(QApplication::translate("CppTools::QuickFix", "Convert to Hexadecimal")); + op->setPriority(priority); + result.append(op); + } + + if (value != 0) { + if (!(numberLength > 1 && str[0] == '0' && str[1] != 'x' && str[1] != 'X')) { /* - Convert integer literal to hex representation. + Convert integer literal to octal representation. Replace 32 - 040 - With 0x20 - + With + 040 */ QString replacement; - replacement.sprintf("0x%lX", value); - QuickFixOperation::Ptr op(new ConvertNumeric(interface, start, start + numberLength, replacement)); - op->setDescription(QApplication::translate("CppTools::QuickFix", "Convert to Hexadecimal")); + replacement.sprintf("0%lo", value); + QuickFixOperation::Ptr op( + new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement)); + op->setDescription(QApplication::translate("CppTools::QuickFix", "Convert to Octal")); op->setPriority(priority); result.append(op); } + } - if (value != 0) { - if (!(numberLength > 1 && str[0] == '0' && str[1] != 'x' && str[1] != 'X')) { - /* - Convert integer literal to octal representation. - Replace - 32 - 0x20 - With - 040 - */ - QString replacement; - replacement.sprintf("0%lo", value); - QuickFixOperation::Ptr op(new ConvertNumeric(interface, start, start + numberLength, replacement)); - op->setDescription(QApplication::translate("CppTools::QuickFix", "Convert to Octal")); - op->setPriority(priority); - result.append(op); - } + if (value != 0 || numeric->isHex()) { + if (!(numberLength > 1 && str[0] != '0')) { + /* + Convert integer literal to decimal representation. + Replace + 0x20 + 040 + With + 32 + */ + QString replacement; + replacement.sprintf("%lu", value); + QuickFixOperation::Ptr op( + new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement)); + op->setDescription(QApplication::translate("CppTools::QuickFix", "Convert to Decimal")); + op->setPriority(priority); + result.append(op); } + } +} - if (value != 0 || numeric->isHex()) { - if (!(numberLength > 1 && str[0] != '0')) { - /* - Convert integer literal to decimal representation. - Replace - 0x20 - 040 - With - 32 - */ - QString replacement; - replacement.sprintf("%lu", value); - QuickFixOperation::Ptr op(new ConvertNumeric(interface, start, start + numberLength, replacement)); - op->setDescription(QApplication::translate("CppTools::QuickFix", "Convert to Decimal")); - op->setPriority(priority); - result.append(op); - } - } +class AddIncludeForForwardDeclarationOp: public CppQuickFixOperation +{ +public: + AddIncludeForForwardDeclarationOp(const CppQuickFixInterface &interface, int priority, + Symbol *fwdClass) + : CppQuickFixOperation(interface, priority) + , fwdClass(fwdClass) + { + setDescription(QApplication::translate("CppTools::QuickFix", + "#include Header File")); } -private: - class ConvertNumeric: public CppQuickFixOperation + void perform() { - public: - ConvertNumeric(const CppQuickFixInterface &interface, - int start, int end, const QString &replacement) - : CppQuickFixOperation(interface) - , start(start) - , end(end) - , replacement(replacement) - {} - - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - - ChangeSet changes; - changes.replace(start, end, replacement); - currentFile->setChangeSet(changes); - currentFile->apply(); - } + QTC_ASSERT(fwdClass != 0, return); + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + + CppTools::SymbolFinder symbolFinder; + if (Class *k = symbolFinder.findMatchingClassDeclaration(fwdClass, snapshot())) { + const QString headerFile = QString::fromUtf8(k->fileName(), k->fileNameLength()); + + // collect the fwd headers + Snapshot fwdHeaders; + fwdHeaders.insert(snapshot().document(headerFile)); + foreach (Document::Ptr doc, snapshot()) { + QFileInfo headerFileInfo(doc->fileName()); + if (doc->globalSymbolCount() == 0 && doc->includes().size() == 1) + fwdHeaders.insert(doc); + else if (headerFileInfo.suffix().isEmpty()) + fwdHeaders.insert(doc); + } - protected: - int start, end; - QString replacement; - }; -}; + DependencyTable dep; + dep.build(fwdHeaders); + QStringList candidates = dep.dependencyTable().value(headerFile); + + const QString className = QString::fromUtf8(k->identifier()->chars()); + + QString best; + foreach (const QString &c, candidates) { + QFileInfo headerFileInfo(c); + if (headerFileInfo.fileName() == className) { + best = c; + break; + } else if (headerFileInfo.fileName().at(0).isUpper()) { + best = c; + // and continue + } else if (! best.isEmpty()) { + if (c.count(QLatin1Char('/')) < best.count(QLatin1Char('/'))) + best = c; + } + } -/* - Can be triggered on a class forward declaration to add the matching #include. + if (best.isEmpty()) + best = headerFile; - Activates on: the name of a forward-declared class or struct -*/ -class FixForwardDeclarationOp: public CppQuickFixFactory -{ -public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) - { - const QList<AST *> &path = interface->path(); + int pos = currentFile->startOf(1); - for (int index = path.size() - 1; index != -1; --index) { - AST *ast = path.at(index); - if (NamedTypeSpecifierAST *namedTy = ast->asNamedTypeSpecifier()) { - if (Symbol *fwdClass = checkName(interface, namedTy->name)) { - result.append(QuickFixOperation::Ptr(new Operation(interface, index, fwdClass))); - return; - } - } else if (ElaboratedTypeSpecifierAST *eTy = ast->asElaboratedTypeSpecifier()) { - if (Symbol *fwdClass = checkName(interface, eTy->name)) { - result.append(QuickFixOperation::Ptr(new Operation(interface, index, fwdClass))); - return; - } + unsigned currentLine = currentFile->cursor().blockNumber() + 1; + unsigned bestLine = 0; + foreach (const Document::Include &incl, assistInterface()->semanticInfo().doc->includes()) { + if (incl.line() < currentLine) + bestLine = incl.line(); } + + if (bestLine) + pos = currentFile->document()->findBlockByNumber(bestLine).position(); + + Utils::ChangeSet changes; + changes.insert(pos, QLatin1String("#include <") + + QFileInfo(best).fileName() + QLatin1String(">\n")); + currentFile->setChangeSet(changes); + currentFile->apply(); } } -protected: static Symbol *checkName(const CppQuickFixInterface &interface, NameAST *ast) { if (ast && interface->isCursorOn(ast)) { @@ -1547,634 +1405,495 @@ protected: } private: - class Operation: public CppQuickFixOperation - { - public: - Operation(const CppQuickFixInterface &interface, int priority, Symbol *fwdClass) - : CppQuickFixOperation(interface, priority) - , fwdClass(fwdClass) - { - setDescription(QApplication::translate("CppTools::QuickFix", - "#include Header File")); - } - - void perform() - { - QTC_ASSERT(fwdClass != 0, return); - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - - CppTools::SymbolFinder symbolFinder; - if (Class *k = symbolFinder.findMatchingClassDeclaration(fwdClass, snapshot())) { - const QString headerFile = QString::fromUtf8(k->fileName(), k->fileNameLength()); - - // collect the fwd headers - Snapshot fwdHeaders; - fwdHeaders.insert(snapshot().document(headerFile)); - foreach (Document::Ptr doc, snapshot()) { - QFileInfo headerFileInfo(doc->fileName()); - if (doc->globalSymbolCount() == 0 && doc->includes().size() == 1) - fwdHeaders.insert(doc); - else if (headerFileInfo.suffix().isEmpty()) - fwdHeaders.insert(doc); - } - - - DependencyTable dep; - dep.build(fwdHeaders); - QStringList candidates = dep.dependencyTable().value(headerFile); - - const QString className = QString::fromUtf8(k->identifier()->chars()); - - QString best; - foreach (const QString &c, candidates) { - QFileInfo headerFileInfo(c); - if (headerFileInfo.fileName() == className) { - best = c; - break; - } else if (headerFileInfo.fileName().at(0).isUpper()) { - best = c; - // and continue - } else if (! best.isEmpty()) { - if (c.count(QLatin1Char('/')) < best.count(QLatin1Char('/'))) - best = c; - } - } - - if (best.isEmpty()) - best = headerFile; + Symbol *fwdClass; +}; - int pos = currentFile->startOf(1); +void AddIncludeForForwardDeclaration::match(const CppQuickFixInterface &interface, + QuickFixOperations &result) +{ + const QList<AST *> &path = interface->path(); - unsigned currentLine = currentFile->cursor().blockNumber() + 1; - unsigned bestLine = 0; - foreach (const Document::Include &incl, assistInterface()->semanticInfo().doc->includes()) { - if (incl.line() < currentLine) - bestLine = incl.line(); - } + for (int index = path.size() - 1; index != -1; --index) { + AST *ast = path.at(index); + if (NamedTypeSpecifierAST *namedTy = ast->asNamedTypeSpecifier()) { + if (Symbol *fwdClass = AddIncludeForForwardDeclarationOp::checkName(interface, namedTy->name)) { + result.append(QuickFixOperation::Ptr( + new AddIncludeForForwardDeclarationOp(interface, index, fwdClass))); + return; + } + } else if (ElaboratedTypeSpecifierAST *eTy = ast->asElaboratedTypeSpecifier()) { + if (Symbol *fwdClass = AddIncludeForForwardDeclarationOp::checkName(interface, eTy->name)) { + result.append(QuickFixOperation::Ptr( + new AddIncludeForForwardDeclarationOp(interface, index, fwdClass))); + return; + } + } + } +} - if (bestLine) - pos = currentFile->document()->findBlockByNumber(bestLine).position(); +class AddLocalDeclarationOp: public CppQuickFixOperation +{ +public: + AddLocalDeclarationOp(const CppQuickFixInterface &interface, + int priority, + const BinaryExpressionAST *binaryAST, + const SimpleNameAST *simpleNameAST) + : CppQuickFixOperation(interface, priority) + , binaryAST(binaryAST) + , simpleNameAST(simpleNameAST) + { + setDescription(QApplication::translate("CppTools::QuickFix", "Add Local Declaration")); + } + void perform() + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + + TypeOfExpression typeOfExpression; + typeOfExpression.init(assistInterface()->semanticInfo().doc, + snapshot(), assistInterface()->context().bindings()); + Scope *scope = currentFile->scopeAt(binaryAST->firstToken()); + const QList<LookupItem> result = + typeOfExpression(currentFile->textOf(binaryAST->right_expression).toUtf8(), + scope, + TypeOfExpression::Preprocess); + + if (! result.isEmpty()) { + SubstitutionEnvironment env; + env.setContext(assistInterface()->context()); + env.switchScope(result.first().scope()); + ClassOrNamespace *con = typeOfExpression.context().lookupType(scope); + if (!con) + con = typeOfExpression.context().globalNamespace(); + UseMinimalNames q(con); + env.enter(&q); + + Control *control = assistInterface()->context().control().data(); + FullySpecifiedType tn = rewriteType(result.first().type(), &env, control); + + Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview(); + QString ty = oo.prettyType(tn, simpleNameAST->name); + if (! ty.isEmpty()) { Utils::ChangeSet changes; - changes.insert(pos, QLatin1String("#include <") - + QFileInfo(best).fileName() + QLatin1String(">\n")); + changes.replace(currentFile->startOf(binaryAST), + currentFile->endOf(simpleNameAST), + ty); currentFile->setChangeSet(changes); currentFile->apply(); } } + } - private: - Symbol *fwdClass; - }; +private: + const BinaryExpressionAST *binaryAST; + const SimpleNameAST *simpleNameAST; }; -/* - Rewrites - a = foo(); - As - Type a = foo(); - Where Type is the return type of foo() - - Activates on: the assignee, if the type of the right-hand side of the assignment is known. -*/ -class AddLocalDeclarationOp: public CppQuickFixFactory +void AddLocalDeclaration::match(const CppQuickFixInterface &interface, QuickFixOperations &result) { -public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) - { - const QList<AST *> &path = interface->path(); - CppRefactoringFilePtr file = interface->currentFile(); - - for (int index = path.size() - 1; index != -1; --index) { - if (BinaryExpressionAST *binary = path.at(index)->asBinaryExpression()) { - if (binary->left_expression && binary->right_expression && file->tokenAt(binary->binary_op_token).is(T_EQUAL)) { - IdExpressionAST *idExpr = binary->left_expression->asIdExpression(); - if (interface->isCursorOn(binary->left_expression) && idExpr && idExpr->name->asSimpleName() != 0) { - SimpleNameAST *nameAST = idExpr->name->asSimpleName(); - const QList<LookupItem> results = interface->context().lookup(nameAST->name, file->scopeAt(nameAST->firstToken())); - Declaration *decl = 0; - foreach (const LookupItem &r, results) { - if (! r.declaration()) - continue; - else if (Declaration *d = r.declaration()->asDeclaration()) { - if (! d->type()->isFunctionType()) { - decl = d; - break; - } + const QList<AST *> &path = interface->path(); + CppRefactoringFilePtr file = interface->currentFile(); + + for (int index = path.size() - 1; index != -1; --index) { + if (BinaryExpressionAST *binary = path.at(index)->asBinaryExpression()) { + if (binary->left_expression && binary->right_expression && file->tokenAt(binary->binary_op_token).is(T_EQUAL)) { + IdExpressionAST *idExpr = binary->left_expression->asIdExpression(); + if (interface->isCursorOn(binary->left_expression) && idExpr && idExpr->name->asSimpleName() != 0) { + SimpleNameAST *nameAST = idExpr->name->asSimpleName(); + const QList<LookupItem> results = interface->context().lookup(nameAST->name, file->scopeAt(nameAST->firstToken())); + Declaration *decl = 0; + foreach (const LookupItem &r, results) { + if (! r.declaration()) + continue; + else if (Declaration *d = r.declaration()->asDeclaration()) { + if (! d->type()->isFunctionType()) { + decl = d; + break; } } + } - if (! decl) { - result.append(QuickFixOperation::Ptr( - new Operation(interface, index, binary, nameAST))); - return; - } + if (! decl) { + result.append(QuickFixOperation::Ptr( + new AddLocalDeclarationOp(interface, index, binary, nameAST))); + return; } } } } } +} -private: - class Operation: public CppQuickFixOperation +class ConvertToCamelCaseOp: public CppQuickFixOperation +{ +public: + ConvertToCamelCaseOp(const CppQuickFixInterface &interface, int priority, + const QString &newName) + : CppQuickFixOperation(interface, priority) + , m_name(newName) { - public: - Operation(const CppQuickFixInterface &interface, - int priority, - const BinaryExpressionAST *binaryAST, - const SimpleNameAST *simpleNameAST) - : CppQuickFixOperation(interface, priority) - , binaryAST(binaryAST) - , simpleNameAST(simpleNameAST) - { - setDescription(QApplication::translate("CppTools::QuickFix", "Add Local Declaration")); - } + setDescription(QApplication::translate("CppTools::QuickFix", + "Convert to Camel Case")); + } - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - - TypeOfExpression typeOfExpression; - typeOfExpression.init(assistInterface()->semanticInfo().doc, - snapshot(), assistInterface()->context().bindings()); - Scope *scope = currentFile->scopeAt(binaryAST->firstToken()); - const QList<LookupItem> result = - typeOfExpression(currentFile->textOf(binaryAST->right_expression).toUtf8(), - scope, - TypeOfExpression::Preprocess); - - if (! result.isEmpty()) { - SubstitutionEnvironment env; - env.setContext(assistInterface()->context()); - env.switchScope(result.first().scope()); - ClassOrNamespace *con = typeOfExpression.context().lookupType(scope); - if (!con) - con = typeOfExpression.context().globalNamespace(); - UseMinimalNames q(con); - env.enter(&q); - - Control *control = assistInterface()->context().control().data(); - FullySpecifiedType tn = rewriteType(result.first().type(), &env, control); - - Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview(); - QString ty = oo.prettyType(tn, simpleNameAST->name); - if (! ty.isEmpty()) { - Utils::ChangeSet changes; - changes.replace(currentFile->startOf(binaryAST), - currentFile->endOf(simpleNameAST), - ty); - currentFile->setChangeSet(changes); - currentFile->apply(); - } + void perform() + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + + for (int i = 1; i < m_name.length(); ++i) { + QCharRef c = m_name[i]; + if (c.isUpper()) { + c = c.toLower(); + } else if (i < m_name.length() - 1 + && isConvertibleUnderscore(m_name, i)) { + m_name.remove(i, 1); + m_name[i] = m_name.at(i).toUpper(); } } + static_cast<CppEditor::Internal::CPPEditorWidget*>(assistInterface()->editor())->renameUsagesNow(m_name); + } - private: - const BinaryExpressionAST *binaryAST; - const SimpleNameAST *simpleNameAST; - }; + static bool isConvertibleUnderscore(const QString &name, int pos) + { + return name.at(pos) == QLatin1Char('_') && name.at(pos+1).isLetter() + && !(pos == 1 && name.at(0) == QLatin1Char('m')); + } + +private: + QString m_name; }; -/** - * Turns "an_example_symbol" into "anExampleSymbol" and - * "AN_EXAMPLE_SYMBOL" into "AnExampleSymbol". - * - * Activates on: identifiers - */ -class ToCamelCaseConverter : public CppQuickFixFactory +void ConvertToCamelCase::match(const CppQuickFixInterface &interface, QuickFixOperations &result) { -public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) - { - const QList<AST *> &path = interface->path(); + const QList<AST *> &path = interface->path(); - if (path.isEmpty()) - return; + if (path.isEmpty()) + return; - AST * const ast = path.last(); - const Name *name = 0; - if (const NameAST * const nameAst = ast->asName()) { - if (nameAst->name && nameAst->name->asNameId()) - name = nameAst->name; - } else if (const NamespaceAST * const namespaceAst = ast->asNamespace()) { - name = namespaceAst->symbol->name(); - } + AST * const ast = path.last(); + const Name *name = 0; + if (const NameAST * const nameAst = ast->asName()) { + if (nameAst->name && nameAst->name->asNameId()) + name = nameAst->name; + } else if (const NamespaceAST * const namespaceAst = ast->asNamespace()) { + name = namespaceAst->symbol->name(); + } - if (!name) - return; + if (!name) + return; - QString newName = QString::fromUtf8(name->identifier()->chars()); - if (newName.length() < 3) + QString newName = QString::fromUtf8(name->identifier()->chars()); + if (newName.length() < 3) + return; + for (int i = 1; i < newName.length() - 1; ++i) { + if (ConvertToCamelCaseOp::isConvertibleUnderscore(newName, i)) { + result.append(QuickFixOperation::Ptr( + new ConvertToCamelCaseOp(interface, path.size() - 1, newName))); return; - for (int i = 1; i < newName.length() - 1; ++i) { - if (Operation::isConvertibleUnderscore(newName, i)) { - result.append(QuickFixOperation::Ptr(new Operation(interface, path.size() - 1, newName))); - return; - } } } +} -private: - class Operation: public CppQuickFixOperation +class AddIncludeForUndefinedIdentifierOp: public CppQuickFixOperation +{ +public: + AddIncludeForUndefinedIdentifierOp(const CppQuickFixInterface &interface, int priority, + const QString &include) + : CppQuickFixOperation(interface, priority) + , m_include(include) { - public: - Operation(const CppQuickFixInterface &interface, int priority, const QString &newName) - : CppQuickFixOperation(interface, priority) - , m_name(newName) - { - setDescription(QApplication::translate("CppTools::QuickFix", - "Convert to Camel Case")); - } - - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - - for (int i = 1; i < m_name.length(); ++i) { - QCharRef c = m_name[i]; - if (c.isUpper()) { - c = c.toLower(); - } else if (i < m_name.length() - 1 - && isConvertibleUnderscore(m_name, i)) { - m_name.remove(i, 1); - m_name[i] = m_name.at(i).toUpper(); - } - } - static_cast<CppEditor::Internal::CPPEditorWidget*>(assistInterface()->editor())->renameUsagesNow(m_name); - } + setDescription(QApplication::translate("CppTools::QuickFix", + "Add #include %1").arg(m_include)); + } - static bool isConvertibleUnderscore(const QString &name, int pos) - { - return name.at(pos) == QLatin1Char('_') && name.at(pos+1).isLetter() - && !(pos == 1 && name.at(0) == QLatin1Char('m')); - } + void perform() + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr file = refactoring.file(fileName()); + + // find location of last include in file + QList<Document::Include> includes = file->cppDocument()->includes(); + unsigned lastIncludeLine = 0; + foreach (const Document::Include &include, includes) { + if (include.line() > lastIncludeLine) + lastIncludeLine = include.line(); + } + + // add include + const int insertPos = file->position(lastIncludeLine + 1, 1) - 1; + ChangeSet changes; + changes.insert(insertPos, QLatin1String("\n#include ") + m_include); + file->setChangeSet(changes); + file->apply(); + } - private: - QString m_name; - }; +private: + QString m_include; }; -/** - * Adds an include for an undefined identifier. - */ -class IncludeAdder : public CppQuickFixFactory +void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interface, + QuickFixOperations &result) { -public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) - { - CppClassesFilter *classesFilter = ExtensionSystem::PluginManager::getObject<CppClassesFilter>(); - if (!classesFilter) - return; + CppClassesFilter *classesFilter = ExtensionSystem::PluginManager::getObject<CppClassesFilter>(); + if (!classesFilter) + return; - const QList<AST *> &path = interface->path(); + const QList<AST *> &path = interface->path(); - if (path.isEmpty()) - return; + if (path.isEmpty()) + return; - // find the largest enclosing Name - const NameAST *enclosingName = 0; - const SimpleNameAST *innermostName = 0; - for (int i = path.size() - 1; i >= 0; --i) { - if (NameAST *nameAst = path.at(i)->asName()) { - enclosingName = nameAst; - if (!innermostName) { - innermostName = nameAst->asSimpleName(); - if (!innermostName) - return; - } - } else { - break; + // find the largest enclosing Name + const NameAST *enclosingName = 0; + const SimpleNameAST *innermostName = 0; + for (int i = path.size() - 1; i >= 0; --i) { + if (NameAST *nameAst = path.at(i)->asName()) { + enclosingName = nameAst; + if (!innermostName) { + innermostName = nameAst->asSimpleName(); + if (!innermostName) + return; } + } else { + break; } - if (!enclosingName || !enclosingName->name) - return; + } + if (!enclosingName || !enclosingName->name) + return; - // find the enclosing scope - unsigned line, column; - const Document::Ptr &doc = interface->semanticInfo().doc; - doc->translationUnit()->getTokenStartPosition(enclosingName->firstToken(), &line, &column); - Scope *scope = doc->scopeAt(line, column); - if (!scope) - return; + // find the enclosing scope + unsigned line, column; + const Document::Ptr &doc = interface->semanticInfo().doc; + doc->translationUnit()->getTokenStartPosition(enclosingName->firstToken(), &line, &column); + Scope *scope = doc->scopeAt(line, column); + if (!scope) + return; - // check if the name resolves to something - QList<LookupItem> existingResults = interface->context().lookup(enclosingName->name, scope); - if (!existingResults.isEmpty()) - return; + // check if the name resolves to something + QList<LookupItem> existingResults = interface->context().lookup(enclosingName->name, scope); + if (!existingResults.isEmpty()) + return; - const QString &className = Overview().prettyName(innermostName->name); - if (className.isEmpty()) - return; + const QString &className = Overview().prettyName(innermostName->name); + if (className.isEmpty()) + return; - // find the include paths - QStringList includePaths; - CppModelManagerInterface *modelManager = CppModelManagerInterface::instance(); - QList<CppModelManagerInterface::ProjectInfo> projectInfos = modelManager->projectInfos(); - bool inProject = false; - foreach (const CppModelManagerInterface::ProjectInfo &info, projectInfos) { - foreach (CppModelManagerInterface::ProjectPart::Ptr part, info.projectParts()) { - if (part->sourceFiles.contains(doc->fileName()) || part->objcSourceFiles.contains(doc->fileName()) || part->headerFiles.contains(doc->fileName())) { - inProject = true; - includePaths += part->includePaths; - } + // find the include paths + QStringList includePaths; + CppModelManagerInterface *modelManager = CppModelManagerInterface::instance(); + QList<CppModelManagerInterface::ProjectInfo> projectInfos = modelManager->projectInfos(); + bool inProject = false; + foreach (const CppModelManagerInterface::ProjectInfo &info, projectInfos) { + foreach (CppModelManagerInterface::ProjectPart::Ptr part, info.projectParts()) { + if (part->sourceFiles.contains(doc->fileName()) || part->objcSourceFiles.contains(doc->fileName()) || part->headerFiles.contains(doc->fileName())) { + inProject = true; + includePaths += part->includePaths; } } - if (!inProject) { - // better use all include paths than none - foreach (const CppModelManagerInterface::ProjectInfo &info, projectInfos) { - foreach (CppModelManagerInterface::ProjectPart::Ptr part, info.projectParts()) - includePaths += part->includePaths; - } + } + if (!inProject) { + // better use all include paths than none + foreach (const CppModelManagerInterface::ProjectInfo &info, projectInfos) { + foreach (CppModelManagerInterface::ProjectPart::Ptr part, info.projectParts()) + includePaths += part->includePaths; } + } - // find a include file through the locator - QFutureInterface<Locator::FilterEntry> dummyInterface; - QList<Locator::FilterEntry> matches = classesFilter->matchesFor(dummyInterface, className); - bool classExists = false; - foreach (const Locator::FilterEntry &entry, matches) { - const ModelItemInfo info = entry.internalData.value<ModelItemInfo>(); - if (info.symbolName != className) - continue; - classExists = true; - const QString &fileName = info.fileName; - const QFileInfo fileInfo(fileName); - - // find the shortest way to include fileName given the includePaths - QString shortestInclude; - - if (fileInfo.path() == QFileInfo(doc->fileName()).path()) { - shortestInclude = QLatin1Char('"') + fileInfo.fileName() + QLatin1Char('"'); - } else { - foreach (const QString &includePath, includePaths) { - if (!fileName.startsWith(includePath)) - continue; - QString relativePath = fileName.mid(includePath.size()); - if (!relativePath.isEmpty() && relativePath.at(0) == QLatin1Char('/')) - relativePath = relativePath.mid(1); - if (shortestInclude.isEmpty() || relativePath.size() + 2 < shortestInclude.size()) - shortestInclude = QLatin1Char('<') + relativePath + QLatin1Char('>'); - } + // find a include file through the locator + QFutureInterface<Locator::FilterEntry> dummyInterface; + QList<Locator::FilterEntry> matches = classesFilter->matchesFor(dummyInterface, className); + bool classExists = false; + foreach (const Locator::FilterEntry &entry, matches) { + const ModelItemInfo info = entry.internalData.value<ModelItemInfo>(); + if (info.symbolName != className) + continue; + classExists = true; + const QString &fileName = info.fileName; + const QFileInfo fileInfo(fileName); + + // find the shortest way to include fileName given the includePaths + QString shortestInclude; + + if (fileInfo.path() == QFileInfo(doc->fileName()).path()) { + shortestInclude = QLatin1Char('"') + fileInfo.fileName() + QLatin1Char('"'); + } else { + foreach (const QString &includePath, includePaths) { + if (!fileName.startsWith(includePath)) + continue; + QString relativePath = fileName.mid(includePath.size()); + if (!relativePath.isEmpty() && relativePath.at(0) == QLatin1Char('/')) + relativePath = relativePath.mid(1); + if (shortestInclude.isEmpty() || relativePath.size() + 2 < shortestInclude.size()) + shortestInclude = QLatin1Char('<') + relativePath + QLatin1Char('>'); } - - if (!shortestInclude.isEmpty()) - result += CppQuickFixOperation::Ptr(new Operation(interface, 0, shortestInclude)); } - // for QSomething, propose a <QSomething> include -- if such a class was in the locator - if (classExists - && className.size() > 2 - && className.at(0) == QLatin1Char('Q') - && className.at(1).isUpper()) { - const QString include = QLatin1Char('<') + className + QLatin1Char('>'); - result += CppQuickFixOperation::Ptr(new Operation(interface, 1, include)); + if (!shortestInclude.isEmpty()) { + result += CppQuickFixOperation::Ptr( + new AddIncludeForUndefinedIdentifierOp(interface, 0, shortestInclude)); } } -private: - class Operation: public CppQuickFixOperation - { - public: - Operation(const CppQuickFixInterface &interface, int priority, const QString &include) - : CppQuickFixOperation(interface, priority) - , m_include(include) - { - setDescription(QApplication::translate("CppTools::QuickFix", - "Add #include %1").arg(m_include)); - } - - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr file = refactoring.file(fileName()); - - // find location of last include in file - QList<Document::Include> includes = file->cppDocument()->includes(); - unsigned lastIncludeLine = 0; - foreach (const Document::Include &include, includes) { - if (include.line() > lastIncludeLine) - lastIncludeLine = include.line(); - } - - // add include - const int insertPos = file->position(lastIncludeLine + 1, 1) - 1; - ChangeSet changes; - changes.insert(insertPos, QLatin1String("\n#include ") + m_include); - file->setChangeSet(changes); - file->apply(); - } - - private: - QString m_include; - }; -}; - -/** - * Switches places of the parameter declaration under cursor - * with the next or the previous one in the parameter declaration list - * - * Activates on: parameter declarations - */ + // for QSomething, propose a <QSomething> include -- if such a class was in the locator + if (classExists + && className.size() > 2 + && className.at(0) == QLatin1Char('Q') + && className.at(1).isUpper()) { + const QString include = QLatin1Char('<') + className + QLatin1Char('>'); + result += CppQuickFixOperation::Ptr( + new AddIncludeForUndefinedIdentifierOp(interface, 1, include)); + } +} -class RearrangeParamDeclList : public CppQuickFixFactory +class RearrangeParamDeclarationListOp: public CppQuickFixOperation { public: - enum Target - { - TargetPrevious, - TargetNext - }; + enum Target { TargetPrevious, TargetNext }; -public: - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) + RearrangeParamDeclarationListOp(const CppQuickFixInterface &interface, AST *currentParam, + AST *targetParam, Target target) + : CppQuickFixOperation(interface) + , m_currentParam(currentParam) + , m_targetParam(targetParam) { - const QList<AST *> path = interface->path(); - - ParameterDeclarationAST *paramDecl = 0; - int index = path.size() - 1; - for (; index != -1; --index) { - paramDecl = path.at(index)->asParameterDeclaration(); - if (paramDecl) - break; - } - - if (index < 1) - return; + QString targetString; + if (target == TargetPrevious) + targetString = QApplication::translate("CppTools::QuickFix", + "Switch with Previous Parameter"); + else + targetString = QApplication::translate("CppTools::QuickFix", + "Switch with Next Parameter"); + setDescription(targetString); + } - ParameterDeclarationClauseAST *paramDeclClause = path.at(index-1)->asParameterDeclarationClause(); - QTC_ASSERT(paramDeclClause && paramDeclClause->parameter_declaration_list, return); + void perform() + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + + int targetEndPos = currentFile->endOf(m_targetParam); + ChangeSet changes; + changes.flip(currentFile->startOf(m_currentParam), currentFile->endOf(m_currentParam), + currentFile->startOf(m_targetParam), targetEndPos); + currentFile->setChangeSet(changes); + currentFile->setOpenEditor(false, targetEndPos); + currentFile->apply(); + } - ParameterDeclarationListAST *paramListNode = paramDeclClause->parameter_declaration_list; - ParameterDeclarationListAST *prevParamListNode = 0; - while (paramListNode) { - if (paramDecl == paramListNode->value) - break; - prevParamListNode = paramListNode; - paramListNode = paramListNode->next; - } +private: + AST *m_currentParam; + AST *m_targetParam; +}; - if (!paramListNode) - return; +void RearrangeParamDeclarationList::match(const CppQuickFixInterface &interface, + QuickFixOperations &result) +{ + const QList<AST *> path = interface->path(); - if (prevParamListNode) - result.append(CppQuickFixOperation::Ptr(new Operation(interface, paramListNode->value, - prevParamListNode->value, TargetPrevious))); - if (paramListNode->next) - result.append(CppQuickFixOperation::Ptr(new Operation(interface, paramListNode->value, - paramListNode->next->value, TargetNext))); + ParameterDeclarationAST *paramDecl = 0; + int index = path.size() - 1; + for (; index != -1; --index) { + paramDecl = path.at(index)->asParameterDeclaration(); + if (paramDecl) + break; } -private: - class Operation: public CppQuickFixOperation - { - public: - Operation(const CppQuickFixInterface &interface, - AST *currentParam, AST *targetParam, - Target target) - : CppQuickFixOperation(interface) - , m_currentParam(currentParam) - , m_targetParam(targetParam) - { - QString targetString; - if (target == TargetPrevious) - targetString = QApplication::translate("CppTools::QuickFix", - "Switch with Previous Parameter"); - else - targetString = QApplication::translate("CppTools::QuickFix", - "Switch with Next Parameter"); - setDescription(targetString); - } + if (index < 1) + return; - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + ParameterDeclarationClauseAST *paramDeclClause = path.at(index-1)->asParameterDeclarationClause(); + QTC_ASSERT(paramDeclClause && paramDeclClause->parameter_declaration_list, return); - int targetEndPos = currentFile->endOf(m_targetParam); - ChangeSet changes; - changes.flip(currentFile->startOf(m_currentParam), currentFile->endOf(m_currentParam), - currentFile->startOf(m_targetParam), targetEndPos); - currentFile->setChangeSet(changes); - currentFile->setOpenEditor(false, targetEndPos); - currentFile->apply(); - } + ParameterDeclarationListAST *paramListNode = paramDeclClause->parameter_declaration_list; + ParameterDeclarationListAST *prevParamListNode = 0; + while (paramListNode) { + if (paramDecl == paramListNode->value) + break; + prevParamListNode = paramListNode; + paramListNode = paramListNode->next; + } - private: - AST *m_currentParam; - AST *m_targetParam; - }; -}; + if (!paramListNode) + return; -/** - * Reformats a pointer, reference or rvalue reference type/declaration. - * - * Works also with selections (except when the cursor is not on any AST). - * - * Activates on: simple declarations, parameters and return types of function - * declarations and definitions, control flow statements (if, - * while, for, foreach) with declarations. - */ -class PointerDeclarationFormatterOp : public CppQuickFixFactory + if (prevParamListNode) + result.append(CppQuickFixOperation::Ptr( + new RearrangeParamDeclarationListOp(interface, paramListNode->value, + prevParamListNode->value, RearrangeParamDeclarationListOp::TargetPrevious))); + if (paramListNode->next) + result.append(CppQuickFixOperation::Ptr( + new RearrangeParamDeclarationListOp(interface, paramListNode->value, + paramListNode->next->value, RearrangeParamDeclarationListOp::TargetNext))); +} + +class ReformatPointerDeclarationOp: public CppQuickFixOperation { public: - typedef Utils::ChangeSet ChangeSet; - - void match(const CppQuickFixInterface &interface, QuickFixOperations &result) + ReformatPointerDeclarationOp(const CppQuickFixInterface &interface, + const Utils::ChangeSet change) + : CppQuickFixOperation(interface) + , m_change(change) { - const QList<AST *> &path = interface->path(); - CppRefactoringFilePtr file = interface->currentFile(); - - Overview overview = CppCodeStyleSettings::currentProjectCodeStyleOverview(); - overview.showArgumentNames = true; - overview.showReturnTypes = true; - - const QTextCursor cursor = file->cursor(); - ChangeSet change; - PointerDeclarationFormatter formatter(file, overview, - PointerDeclarationFormatter::RespectCursor); - - if (cursor.hasSelection()) { - // This will no work always as expected since this method is only called if - // interface-path() is not empty. If the user selects the whole document via - // ctrl-a and there is an empty line in the end, then the cursor is not on - // any AST and therefore no quick fix will be triggered. - change = formatter.format(file->cppDocument()->translationUnit()->ast()); - if (! change.isEmpty()) - result.append(QuickFixOperation::Ptr(new Operation(interface, change))); - } else { - for (int index = path.size() - 1; index >= 0; --index) { - AST *ast = path.at(index); - - change = formatter.format(ast); - if (! change.isEmpty()) { - result.append(QuickFixOperation::Ptr(new Operation(interface, change))); - return; - } - } - } + QString description; + if (m_change.operationList().size() == 1) { + description = QApplication::translate("CppTools::QuickFix", + "Reformat to \"%1\"").arg(m_change.operationList().first().text); + } else { // > 1 + description = QApplication::translate("CppTools::QuickFix", + "Reformat Pointers or References"); + } + setDescription(description); } -private: - class Operation: public CppQuickFixOperation + void perform() { - public: - Operation(const CppQuickFixInterface &interface, const ChangeSet change) - : CppQuickFixOperation(interface) - , m_change(change) - { - QString description; - if (m_change.operationList().size() == 1) { - description = QApplication::translate("CppTools::QuickFix", - "Reformat to \"%1\"").arg(m_change.operationList().first().text); - } else { // > 1 - description = QApplication::translate("CppTools::QuickFix", - "Reformat Pointers or References"); - } - setDescription(description); - } - - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.file(fileName()); - currentFile->setChangeSet(m_change); - currentFile->apply(); - } + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + currentFile->setChangeSet(m_change); + currentFile->apply(); + } - private: - ChangeSet m_change; - }; +private: + Utils::ChangeSet m_change; }; -} // end of anonymous namespace - -void registerQuickFixes(ExtensionSystem::IPlugin *plugIn) +void ReformatPointerDeclaration::match(const CppQuickFixInterface &interface, + QuickFixOperations &result) { - plugIn->addAutoReleasedObject(new UseInverseOp); - plugIn->addAutoReleasedObject(new FlipBinaryOp); - plugIn->addAutoReleasedObject(new RewriteLogicalAndOp); - plugIn->addAutoReleasedObject(new SplitSimpleDeclarationOp); - plugIn->addAutoReleasedObject(new AddBracesToIfOp); - plugIn->addAutoReleasedObject(new MoveDeclarationOutOfIfOp); - plugIn->addAutoReleasedObject(new MoveDeclarationOutOfWhileOp); - plugIn->addAutoReleasedObject(new SplitIfStatementOp); - plugIn->addAutoReleasedObject(new WrapStringLiteral); - plugIn->addAutoReleasedObject(new TranslateStringLiteral); - plugIn->addAutoReleasedObject(new CStringToNSString); - plugIn->addAutoReleasedObject(new ConvertNumericLiteral); - plugIn->addAutoReleasedObject(new CompleteSwitchCaseStatement); - plugIn->addAutoReleasedObject(new FixForwardDeclarationOp); - plugIn->addAutoReleasedObject(new AddLocalDeclarationOp); - plugIn->addAutoReleasedObject(new ToCamelCaseConverter); - plugIn->addAutoReleasedObject(new InsertQtPropertyMembers); - plugIn->addAutoReleasedObject(new DeclFromDef); - plugIn->addAutoReleasedObject(new DefFromDecl); - plugIn->addAutoReleasedObject(new ApplyDeclDefLinkChanges); - plugIn->addAutoReleasedObject(new IncludeAdder); - plugIn->addAutoReleasedObject(new ExtractFunction); - plugIn->addAutoReleasedObject(new GetterSetter); - plugIn->addAutoReleasedObject(new RearrangeParamDeclList); - plugIn->addAutoReleasedObject(new PointerDeclarationFormatterOp); + const QList<AST *> &path = interface->path(); + CppRefactoringFilePtr file = interface->currentFile(); + + Overview overview = CppCodeStyleSettings::currentProjectCodeStyleOverview(); + overview.showArgumentNames = true; + overview.showReturnTypes = true; + + const QTextCursor cursor = file->cursor(); + Utils::ChangeSet change; + PointerDeclarationFormatter formatter(file, overview, + PointerDeclarationFormatter::RespectCursor); + + if (cursor.hasSelection()) { + // This will no work always as expected since this method is only called if + // interface-path() is not empty. If the user selects the whole document via + // ctrl-a and there is an empty line in the end, then the cursor is not on + // any AST and therefore no quick fix will be triggered. + change = formatter.format(file->cppDocument()->translationUnit()->ast()); + if (! change.isEmpty()) { + result.append(QuickFixOperation::Ptr( + new ReformatPointerDeclarationOp(interface, change))); + } + } else { + for (int index = path.size() - 1; index >= 0; --index) { + AST *ast = path.at(index); + + change = formatter.format(ast); + if (! change.isEmpty()) { + result.append(QuickFixOperation::Ptr( + new ReformatPointerDeclarationOp(interface, change))); + return; + } + } + } } diff --git a/src/plugins/cppeditor/cppquickfixes.h b/src/plugins/cppeditor/cppquickfixes.h new file mode 100644 index 0000000000..967f8a83bf --- /dev/null +++ b/src/plugins/cppeditor/cppquickfixes.h @@ -0,0 +1,393 @@ +/**************************************************************************** +** +** Copyright (C) 2013 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. +** +****************************************************************************/ + +#ifndef CPPQUICKFIXES_H +#define CPPQUICKFIXES_H + +#include "cppquickfix.h" + +#include <cpptools/cpprefactoringchanges.h> +#include <extensionsystem/iplugin.h> + +#include <AST.h> +#include <ASTMatcher.h> + +QT_BEGIN_NAMESPACE +class QByteArray; +class QString; +template <class> class QList; +QT_END_NAMESPACE + +using namespace CppTools; +using namespace CPlusPlus; +using namespace TextEditor; + +namespace CppEditor { +namespace Internal { + +void registerQuickFixes(ExtensionSystem::IPlugin *plugIn); + +/*! + Adds an include for an undefined identifier. + + Activates on: the undefined identifier +*/ +class AddIncludeForUndefinedIdentifier : public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Can be triggered on a class forward declaration to add the matching #include. + + Activates on: the name of a forward-declared class or struct +*/ +class AddIncludeForForwardDeclaration: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Rewrite + a op b + + As + b flipop a + + Activates on: <= < > >= == != && || +*/ +class FlipLogicalOperands: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Rewrite + a op b -> !(a invop b) + (a op b) -> !(a invop b) + !(a op b) -> (a invob b) + + Activates on: <= < > >= == != +*/ +class InverseLogicalComparison: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Rewrite + !a && !b + + As + !(a || b) + + Activates on: && +*/ +class RewriteLogicalAnd: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); + +private: + ASTMatcher matcher; +}; + +/*! + Replace + "abcd" + QLatin1String("abcd") + QLatin1Literal("abcd") + + With + @"abcd" + + Activates on: the string literal, if the file type is a Objective-C(++) file. +*/ +class ConvertCStringToNSString: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Base class for converting numeric literals between decimal, octal and hex. + Does the base check for the specific ones and parses the number. + + Test cases: + 0xFA0Bu; + 0X856A; + 298.3; + 199; + 074; + 199L; + 074L; + -199; + -017; + 0783; // invalid octal + 0; // border case, allow only hex<->decimal + + Activates on: numeric literals +*/ +class ConvertNumericLiteral: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Replace + "abcd" + + With + tr("abcd") or + QCoreApplication::translate("CONTEXT", "abcd") or + QT_TRANSLATE_NOOP("GLOBAL", "abcd") + + depending on what is available. + + Activates on: the string literal +*/ +class TranslateStringLiteral: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Replace + "abcd" -> QLatin1String("abcd") + @"abcd" -> QLatin1String("abcd") (Objective C) + 'a' -> QLatin1Char('a') + 'a' -> "a" + "a" -> 'a' or QLatin1Char('a') (Single character string constants) + "\n" -> '\n', QLatin1Char('\n') + + Except if they are already enclosed in + QLatin1Char, QT_TRANSLATE_NOOP, tr, + trUtf8, QLatin1Literal, QLatin1String + + Activates on: the string or character literal +*/ + +class WrapStringLiteral: public CppQuickFixFactory +{ +public: + enum ActionFlags { + EncloseInQLatin1CharAction = 0x1, + EncloseInQLatin1StringAction = 0x2, + EncloseInQStringLiteralAction = 0x4, + EncloseActionMask = EncloseInQLatin1CharAction + | EncloseInQLatin1StringAction | EncloseInQStringLiteralAction, + TranslateTrAction = 0x8, + TranslateQCoreApplicationAction = 0x10, + TranslateNoopAction = 0x20, + TranslationMask = TranslateTrAction + | TranslateQCoreApplicationAction | TranslateNoopAction, + RemoveObjectiveCAction = 0x40, + ConvertEscapeSequencesToCharAction = 0x100, + ConvertEscapeSequencesToStringAction = 0x200, + SingleQuoteAction = 0x400, + DoubleQuoteAction = 0x800 + }; + + enum Type { TypeString, TypeObjCString, TypeChar, TypeNone }; + + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); + + static QString replacement(unsigned actions); + static QByteArray stringToCharEscapeSequences(const QByteArray &content); + static QByteArray charToStringEscapeSequences(const QByteArray &content); + + static ExpressionAST *analyze(const QList<AST *> &path, const CppRefactoringFilePtr &file, + Type *type, + QByteArray *enclosingFunction = 0, + CallAST **enclosingFunctionCall = 0); +}; + +/*! + Turns "an_example_symbol" into "anExampleSymbol" and + "AN_EXAMPLE_SYMBOL" into "AnExampleSymbol". + + Activates on: identifiers +*/ +class ConvertToCamelCase : public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Replace + if (Type name = foo()) {...} + + With + Type name = foo; + if (name) {...} + + Activates on: the name of the introduced variable +*/ +class MoveDeclarationOutOfIf: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Replace + while (Type name = foo()) {...} + + With + Type name; + while ((name = foo()) != 0) {...} + + Activates on: the name of the introduced variable +*/ +class MoveDeclarationOutOfWhile: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Replace + if (something && something_else) { + } + + with + if (something) + if (something_else) + } + + and + if (something || something_else) + x; + + with + if (something) + x; + else if (something_else) + x; + + Activates on: && or || +*/ +class SplitIfStatement: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Rewrite + int *a, b; + + As + int *a; + int b; + + Activates on: the type or the variable names. +*/ +class SplitSimpleDeclaration: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); + +private: + static bool checkDeclaration(SimpleDeclarationAST *declaration); +}; + +/*! + Rewrites + a = foo(); + + As + Type a = foo(); + + Where Type is the return type of foo() + + Activates on: the assignee, if the type of the right-hand side of the assignment is known. +*/ +class AddLocalDeclaration: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Add curly braces to a if statement that doesn't already contain a + compound statement. I.e. + + if (a) + b; + becomes + if (a) + b; + + Activates on: the if +*/ +class AddBracesToIf: public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Switches places of the parameter declaration under cursor + with the next or the previous one in the parameter declaration list + + Activates on: parameter declarations +*/ +class RearrangeParamDeclarationList : public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +/*! + Reformats a pointer, reference or rvalue reference type/declaration. + + Works also with selections (except when the cursor is not on any AST). + + Activates on: simple declarations, parameters and return types of function + declarations and definitions, control flow statements (if, + while, for, foreach) with declarations. +*/ +class ReformatPointerDeclaration : public CppQuickFixFactory +{ +public: + void match(const CppQuickFixInterface &interface, QuickFixOperations &result); +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPQUICKFIXES_H |