OSDN Git Service

ca619aa0d97a865ebccb3cae2746f711364f4525
[qt-creator-jp/qt-creator-jp.git] / src / plugins / cppeditor / cppcompleteswitch.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "cppcompleteswitch.h"
35
36 #include <cplusplus/Overview.h>
37 #include <cplusplus/TypeOfExpression.h>
38 #include <cpptools/cpprefactoringchanges.h>
39 #include <utils/changeset.h>
40
41 #include <AST.h>
42 #include <ASTVisitor.h>
43 #include <CoreTypes.h>
44 #include <Symbols.h>
45
46 #include <QtGui/QApplication>
47
48 using namespace CPlusPlus;
49 using namespace CppEditor;
50 using namespace CppEditor::Internal;
51 using namespace CppTools;
52 using namespace Utils;
53
54 namespace {
55
56 class CaseStatementCollector : public ASTVisitor
57 {
58 public:
59     CaseStatementCollector(Document::Ptr document, const Snapshot &snapshot,
60                            Scope *scope)
61         : ASTVisitor(document->translationUnit()),
62         document(document),
63         scope(scope)
64     {
65         typeOfExpression.init(document, snapshot);
66     }
67
68     QStringList operator ()(AST *ast)
69     {
70         values.clear();
71         foundCaseStatementLevel = false;
72         accept(ast);
73         return values;
74     }
75
76     bool preVisit(AST *ast) {
77         if (CaseStatementAST *cs = ast->asCaseStatement()) {
78             foundCaseStatementLevel = true;
79             if (ExpressionAST *expression = cs->expression->asIdExpression()) {
80                 QList<LookupItem> candidates = typeOfExpression(expression,
81                                                                 document,
82                                                                 scope);
83                 if (!candidates .isEmpty() && candidates.first().declaration()) {
84                     Symbol *decl = candidates.first().declaration();
85                     values << prettyPrint(LookupContext::fullyQualifiedName(decl));
86                 }
87             }
88             return true;
89         } else if (foundCaseStatementLevel) {
90             return false;
91         }
92         return true;
93     }
94
95     Overview prettyPrint;
96     bool foundCaseStatementLevel;
97     QStringList values;
98     TypeOfExpression typeOfExpression;
99     Document::Ptr document;
100     Scope *scope;
101 };
102
103 class Operation: public CppQuickFixOperation
104 {
105 public:
106     Operation(const CppQuickFixState &state, int priority, CompoundStatementAST *compoundStatement, const QStringList &values)
107         : CppQuickFixOperation(state, priority)
108         , compoundStatement(compoundStatement)
109         , values(values)
110     {
111         setDescription(QApplication::translate("CppTools::QuickFix",
112                                                "Complete Switch Statement"));
113     }
114
115
116     virtual void performChanges(CppRefactoringFile *currentFile, CppRefactoringChanges *)
117     {
118         ChangeSet changes;
119         int start = currentFile->endOf(compoundStatement->lbrace_token);
120         changes.insert(start, QLatin1String("\ncase ")
121                        + values.join(QLatin1String(":\nbreak;\ncase "))
122                        + QLatin1String(":\nbreak;"));
123         currentFile->change(changes);
124         currentFile->indent(currentFile->range(compoundStatement));
125     }
126
127     CompoundStatementAST *compoundStatement;
128     QStringList values;
129 };
130
131 static Enum *findEnum(const QList<LookupItem> &results,
132                       const LookupContext &ctxt)
133 {
134     foreach (const LookupItem &result, results) {
135         const FullySpecifiedType fst = result.type();
136
137         Type *type = result.declaration() ? result.declaration()->type().type()
138                                           : fst.type();
139
140         if (!type)
141             continue;
142         if (Enum *e = type->asEnumType())
143             return e;
144         if (const NamedType *namedType = type->asNamedType()) {
145             const QList<LookupItem> candidates =
146                     ctxt.lookup(namedType->name(), result.scope());
147             return findEnum(candidates, ctxt);
148         }
149     }
150
151     return 0;
152 }
153
154 static Enum *conditionEnum(const CppQuickFixState &state,
155                            SwitchStatementAST *statement)
156 {
157     Block *block = statement->symbol;
158     Scope *scope = state.document()->scopeAt(block->line(), block->column());
159     TypeOfExpression typeOfExpression;
160     typeOfExpression.init(state.document(), state.snapshot());
161     const QList<LookupItem> results = typeOfExpression(statement->condition,
162                                                        state.document(),
163                                                        scope);
164
165     return findEnum(results, typeOfExpression.context());
166 }
167
168 } // end of anonymous namespace
169
170 QList<CppQuickFixOperation::Ptr> CompleteSwitchCaseStatement::match(const CppQuickFixState &state)
171 {
172     const QList<AST *> &path = state.path();
173
174     if (path.isEmpty())
175         return noResult(); // nothing to do
176
177     // look for switch statement
178     for (int depth = path.size() - 1; depth >= 0; --depth) {
179         AST *ast = path.at(depth);
180         SwitchStatementAST *switchStatement = ast->asSwitchStatement();
181         if (switchStatement) {
182             if (!state.isCursorOn(switchStatement->switch_token) || !switchStatement->statement)
183                 return noResult();
184             CompoundStatementAST *compoundStatement = switchStatement->statement->asCompoundStatement();
185             if (!compoundStatement) // we ignore pathologic case "switch (t) case A: ;"
186                 return noResult();
187             // look if the condition's type is an enum
188             if (Enum *e = conditionEnum(state, switchStatement)) {
189                 // check the possible enum values
190                 QStringList values;
191                 Overview prettyPrint;
192                 for (unsigned i = 0; i < e->memberCount(); ++i) {
193                     if (Declaration *decl = e->memberAt(i)->asDeclaration()) {
194                         values << prettyPrint(LookupContext::fullyQualifiedName(decl));
195                     }
196                 }
197                 // Get the used values
198                 Block *block = switchStatement->symbol;
199                 CaseStatementCollector caseValues(state.document(), state.snapshot(),
200                                                   state.document()->scopeAt(block->line(), block->column()));
201                 QStringList usedValues = caseValues(switchStatement);
202                 // save the values that would be added
203                 foreach (const QString &usedValue, usedValues)
204                     values.removeAll(usedValue);
205                 if (values.isEmpty())
206                     return noResult();
207                 else
208                     return singleResult(new Operation(state, depth, compoundStatement, values));
209             }
210
211             return noResult();
212         }
213     }
214
215     return noResult();
216 }