The condition resolving now looks through typedefs and calls.
Task-number: QTCREATORBUG-2051
Reviewed-by: Christian Kamm
--- /dev/null
+/**************************************************************************
+**
+** 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();
+}
--- /dev/null
+/**************************************************************************
+**
+** 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
cppelementevaluator.h \
cppquickfixcollector.h \
cppqtstyleindenter.h \
- cppautocompleter.h
+ cppautocompleter.h \
+ cppcompleteswitch.h
SOURCES += cppplugin.cpp \
cppeditor.cpp \
cpphighlighter.cpp \
cppelementevaluator.cpp \
cppquickfixcollector.cpp \
cppqtstyleindenter.cpp \
- cppautocompleter.cpp
+ cppautocompleter.cpp \
+ cppcompleteswitch.cpp
RESOURCES += cppeditor.qrc
OTHER_FILES += CppEditor.mimetypes.xml
**
**************************************************************************/
+#include "cppcompleteswitch.h"
#include "cppeditor.h"
#include "cppquickfix.h"
#include "cppinsertdecldef.h"
};
};
-/*
- 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:
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);