diff --git a/src/plugins/cppeditor/cppcompleteswitch.cpp b/src/plugins/cppeditor/cppcompleteswitch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e23ee3b5a6594d7bd32e0d05ab927fe0ad2650c2 --- /dev/null +++ b/src/plugins/cppeditor/cppcompleteswitch.cpp @@ -0,0 +1,212 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "cppcompleteswitch.h" + +#include <cplusplus/Overview.h> +#include <cplusplus/TypeOfExpression.h> +#include <cpptools/cpprefactoringchanges.h> +#include <utils/changeset.h> + +#include <AST.h> +#include <ASTVisitor.h> +#include <CoreTypes.h> +#include <Symbols.h> + +#include <QtGui/QApplication> + +using namespace CPlusPlus; +using namespace CppEditor; +using namespace CppEditor::Internal; +using namespace CppTools; +using namespace Utils; + +namespace { + +class CaseStatementCollector : public ASTVisitor +{ +public: + CaseStatementCollector(Document::Ptr document, const Snapshot &snapshot, + Scope *scope) + : ASTVisitor(document->translationUnit()), + document(document), + scope(scope) + { + typeOfExpression.init(document, snapshot); + } + + QStringList operator ()(AST *ast) + { + values.clear(); + foundCaseStatementLevel = false; + accept(ast); + return values; + } + + bool preVisit(AST *ast) { + if (CaseStatementAST *cs = ast->asCaseStatement()) { + foundCaseStatementLevel = true; + if (ExpressionAST *expression = cs->expression->asIdExpression()) { + QList<LookupItem> candidates = typeOfExpression(expression, + document, + scope); + if (!candidates .isEmpty() && candidates.first().declaration()) { + Symbol *decl = candidates.first().declaration(); + values << prettyPrint(LookupContext::fullyQualifiedName(decl)); + } + } + return true; + } else if (foundCaseStatementLevel) { + return false; + } + return true; + } + + Overview prettyPrint; + bool foundCaseStatementLevel; + QStringList values; + TypeOfExpression typeOfExpression; + Document::Ptr document; + Scope *scope; +}; + +class Operation: public CppQuickFixOperation +{ +public: + Operation(const CppQuickFixState &state, int priority, CompoundStatementAST *compoundStatement, const QStringList &values) + : CppQuickFixOperation(state, priority) + , compoundStatement(compoundStatement) + , values(values) + { + setDescription(QApplication::translate("CppTools::QuickFix", + "Complete Switch Statement")); + } + + + virtual void performChanges(CppRefactoringFile *currentFile, CppRefactoringChanges *) + { + ChangeSet changes; + int start = currentFile->endOf(compoundStatement->lbrace_token); + changes.insert(start, QLatin1String("\ncase ") + + values.join(QLatin1String(":\nbreak;\ncase ")) + + QLatin1String(":\nbreak;")); + currentFile->change(changes); + currentFile->indent(currentFile->range(compoundStatement)); + } + + CompoundStatementAST *compoundStatement; + QStringList values; +}; + +static Enum *findEnum(const QList<LookupItem> &results, + const LookupContext &ctxt) +{ + foreach (const LookupItem &result, results) { + const FullySpecifiedType fst = result.type(); + + Type *type = result.declaration() ? result.declaration()->type().type() + : fst.type(); + + if (!type) + continue; + if (Enum *e = type->asEnumType()) + return e; + if (const NamedType *namedType = type->asNamedType()) { + const QList<LookupItem> candidates = + ctxt.lookup(namedType->name(), result.scope()); + return findEnum(candidates, ctxt); + } + } + + return 0; +} + +static Enum *conditionEnum(const CppQuickFixState &state, + SwitchStatementAST *statement) +{ + Block *block = statement->symbol; + Scope *scope = state.document()->scopeAt(block->line(), block->column()); + TypeOfExpression typeOfExpression; + typeOfExpression.init(state.document(), state.snapshot()); + const QList<LookupItem> results = typeOfExpression(statement->condition, + state.document(), + scope); + + return findEnum(results, typeOfExpression.context()); +} + +} // end of anonymous namespace + +QList<CppQuickFixOperation::Ptr> CompleteSwitchCaseStatement::match(const CppQuickFixState &state) +{ + const QList<AST *> &path = state.path(); + + if (path.isEmpty()) + return noResult(); // nothing to do + + // look for switch statement + for (int depth = path.size() - 1; depth >= 0; --depth) { + AST *ast = path.at(depth); + SwitchStatementAST *switchStatement = ast->asSwitchStatement(); + if (switchStatement) { + if (!state.isCursorOn(switchStatement->switch_token) || !switchStatement->statement) + return noResult(); + CompoundStatementAST *compoundStatement = switchStatement->statement->asCompoundStatement(); + if (!compoundStatement) // we ignore pathologic case "switch (t) case A: ;" + return noResult(); + // look if the condition's type is an enum + if (Enum *e = conditionEnum(state, switchStatement)) { + // check the possible enum values + QStringList values; + Overview prettyPrint; + for (unsigned i = 0; i < e->memberCount(); ++i) { + if (Declaration *decl = e->memberAt(i)->asDeclaration()) { + values << prettyPrint(LookupContext::fullyQualifiedName(decl)); + } + } + // Get the used values + Block *block = switchStatement->symbol; + CaseStatementCollector caseValues(state.document(), state.snapshot(), + state.document()->scopeAt(block->line(), block->column())); + QStringList usedValues = caseValues(switchStatement); + // save the values that would be added + foreach (const QString &usedValue, usedValues) + values.removeAll(usedValue); + if (values.isEmpty()) + return noResult(); + else + return singleResult(new Operation(state, depth, compoundStatement, values)); + } + + return noResult(); + } + } + + return noResult(); +} diff --git a/src/plugins/cppeditor/cppcompleteswitch.h b/src/plugins/cppeditor/cppcompleteswitch.h new file mode 100644 index 0000000000000000000000000000000000000000..57772aa31c26bd71f37a9ce9375fd339346a374d --- /dev/null +++ b/src/plugins/cppeditor/cppcompleteswitch.h @@ -0,0 +1,52 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CPPCOMPLETESWITCH_H +#define CPPCOMPLETESWITCH_H + +#include "cppquickfix.h" + +#include <CPlusPlusForwardDeclarations.h> + +namespace CppEditor { +namespace Internal { + +/*! + Adds missing case statements for "switch (enumVariable)" + */ +class CompleteSwitchCaseStatement: public CppQuickFixFactory +{ +public: + virtual QList<CppQuickFixOperation::Ptr> match(const CppQuickFixState &state); +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPCOMPLETESWITCH_H diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index 4ade5624bb6f95bd140b96ada0dfb4a42b6aa7ae..0e6aa03ffdebf4c8757b596ebf7d235b20607ce1 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -24,7 +24,8 @@ HEADERS += cppplugin.h \ cppelementevaluator.h \ cppquickfixcollector.h \ cppqtstyleindenter.h \ - cppautocompleter.h + cppautocompleter.h \ + cppcompleteswitch.h SOURCES += cppplugin.cpp \ cppeditor.cpp \ cpphighlighter.cpp \ @@ -42,6 +43,7 @@ SOURCES += cppplugin.cpp \ cppelementevaluator.cpp \ cppquickfixcollector.cpp \ cppqtstyleindenter.cpp \ - cppautocompleter.cpp + cppautocompleter.cpp \ + cppcompleteswitch.cpp RESOURCES += cppeditor.qrc OTHER_FILES += CppEditor.mimetypes.xml diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 503150e1c967bc34b75a5bce55cd12fdb45dc01b..3165c5f36c3221d6a43b3f2b2d833b0283ef1f61 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -27,6 +27,7 @@ ** **************************************************************************/ +#include "cppcompleteswitch.h" #include "cppeditor.h" #include "cppquickfix.h" #include "cppinsertdecldef.h" @@ -1279,174 +1280,6 @@ private: }; }; -/* - Adds missing case statements for "switch (enumVariable)" -*/ -class CompleteSwitchCaseStatement: public CppQuickFixFactory -{ -public: - virtual QList<CppQuickFixOperation::Ptr> match(const CppQuickFixState &state) - { - const QList<AST *> &path = state.path(); - - if (path.isEmpty()) - return noResult(); // nothing to do - - // look for switch statement - for (int depth = path.size() - 1; depth >= 0; --depth) { - AST *ast = path.at(depth); - SwitchStatementAST *switchStatement = ast->asSwitchStatement(); - if (switchStatement) { - if (!state.isCursorOn(switchStatement->switch_token) || !switchStatement->statement) - return noResult(); - CompoundStatementAST *compoundStatement = switchStatement->statement->asCompoundStatement(); - if (!compoundStatement) // we ignore pathologic case "switch (t) case A: ;" - return noResult(); - // look if the condition's type is an enum - if (Enum *e = conditionEnum(state, switchStatement)) { - // check the possible enum values - QStringList values; - Overview prettyPrint; - for (unsigned i = 0; i < e->memberCount(); ++i) { - if (Declaration *decl = e->memberAt(i)->asDeclaration()) { - values << prettyPrint(LookupContext::fullyQualifiedName(decl)); - } - } - // Get the used values - Block *block = switchStatement->symbol; - CaseStatementCollector caseValues(state.document(), state.snapshot(), - state.document()->scopeAt(block->line(), block->column())); - QStringList usedValues = caseValues(switchStatement); - // save the values that would be added - foreach (const QString &usedValue, usedValues) - values.removeAll(usedValue); - if (values.isEmpty()) - return noResult(); - return singleResult(new Operation(state, depth, compoundStatement, values)); - } - return noResult(); - } - } - - return noResult(); - } - -protected: - Enum *conditionEnum(const CppQuickFixState &state, SwitchStatementAST *statement) - { - Block *block = statement->symbol; - Scope *scope = state.document()->scopeAt(block->line(), block->column()); - TypeOfExpression typeOfExpression; - typeOfExpression.init(state.document(), state.snapshot()); - const QList<LookupItem> results = typeOfExpression(statement->condition, - state.document(), - scope); - - /// - /// \note FIXME: the lookup has at least two problems: the result.declaration() - /// will often be null, (i.e. when the condition is a function call) - /// and the lookups will not look through typedefs. - /// - - foreach (LookupItem result, results) { - FullySpecifiedType fst = result.type(); - if (! result.declaration()) - continue; - if (Enum *e = result.declaration()->type()->asEnumType()) - return e; - if (NamedType *namedType = fst->asNamedType()) { - QList<LookupItem> candidates = - typeOfExpression.context().lookup(namedType->name(), scope); - foreach (const LookupItem &r, candidates) { - if (Symbol *candidate = r.declaration()) { - if (Enum *e = candidate->asEnum()) { - return e; - } - } - } - } - } - return 0; - } - - class CaseStatementCollector : public ASTVisitor - { - public: - CaseStatementCollector(Document::Ptr document, const Snapshot &snapshot, - Scope *scope) - : ASTVisitor(document->translationUnit()), - document(document), - scope(scope) - { - typeOfExpression.init(document, snapshot); - } - - QStringList operator ()(AST *ast) - { - values.clear(); - foundCaseStatementLevel = false; - accept(ast); - return values; - } - - bool preVisit(AST *ast) { - if (CaseStatementAST *cs = ast->asCaseStatement()) { - foundCaseStatementLevel = true; - if (ExpressionAST *expression = cs->expression->asIdExpression()) { - QList<LookupItem> candidates = typeOfExpression(expression, - document, - scope); - if (!candidates .isEmpty() && candidates.first().declaration()) { - Symbol *decl = candidates.first().declaration(); - values << prettyPrint(LookupContext::fullyQualifiedName(decl)); - } - } - return true; - } else if (foundCaseStatementLevel) { - return false; - } - return true; - } - - Overview prettyPrint; - bool foundCaseStatementLevel; - QStringList values; - TypeOfExpression typeOfExpression; - Document::Ptr document; - Scope *scope; - }; - -private: - class Operation: public CppQuickFixOperation - { - public: - Operation(const CppQuickFixState &state, int priority, CompoundStatementAST *compoundStatement, const QStringList &values) - : CppQuickFixOperation(state, priority) - , compoundStatement(compoundStatement) - , values(values) - { - setDescription(QApplication::translate("CppTools::QuickFix", - "Complete Switch Statement")); - } - - - virtual void performChanges(CppRefactoringFile *currentFile, CppRefactoringChanges *) - { - ChangeSet changes; - int start = currentFile->endOf(compoundStatement->lbrace_token); - changes.insert(start, QLatin1String("\ncase ") - + values.join(QLatin1String(":\nbreak;\ncase ")) - + QLatin1String(":\nbreak;")); - currentFile->change(changes); - currentFile->indent(currentFile->range(compoundStatement)); - } - - CompoundStatementAST *compoundStatement; - QStringList values; - }; -}; - - class FixForwardDeclarationOp: public CppQuickFixFactory { public: @@ -1751,8 +1584,7 @@ void CppQuickFixCollector::registerQuickFixes(ExtensionSystem::IPlugin *plugIn) plugIn->addAutoReleasedObject(new TranslateStringLiteral); plugIn->addAutoReleasedObject(new CStringToNSString); plugIn->addAutoReleasedObject(new ConvertNumericLiteral); -// Disabled for now: see the CompleteSwitchCaseStatement class for the reason. -// plugIn->addAutoReleasedObject(new CompleteSwitchCaseStatement); + plugIn->addAutoReleasedObject(new Internal::CompleteSwitchCaseStatement); plugIn->addAutoReleasedObject(new FixForwardDeclarationOp); plugIn->addAutoReleasedObject(new AddLocalDeclarationOp); plugIn->addAutoReleasedObject(new ToCamelCaseConverter);