OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / cpptools / insertionpointlocator.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 "cpptoolsplugin.h"
35 #include "cpprefactoringchanges.h"
36 #include "insertionpointlocator.h"
37
38 #include <AST.h>
39 #include <ASTVisitor.h>
40 #include <TranslationUnit.h>
41
42 #include <coreplugin/icore.h>
43 #include <coreplugin/mimedatabase.h>
44
45 using namespace CPlusPlus;
46 using namespace CppTools;
47
48 namespace {
49
50 static QString generate(InsertionPointLocator::AccessSpec xsSpec)
51 {
52     switch (xsSpec) {
53     default:
54     case InsertionPointLocator::Public:
55         return QLatin1String("public:\n");
56
57     case InsertionPointLocator::Protected:
58         return QLatin1String("protected:\n");
59
60     case InsertionPointLocator::Private:
61         return QLatin1String("private:\n");
62
63     case InsertionPointLocator::PublicSlot:
64         return QLatin1String("public slots:\n");
65
66     case InsertionPointLocator::ProtectedSlot:
67         return QLatin1String("protected slots:\n");
68
69     case InsertionPointLocator::PrivateSlot:
70         return QLatin1String("private slots:\n");
71
72     case InsertionPointLocator::Signals:
73         return QLatin1String("signals:\n");
74     }
75 }
76
77 static int ordering(InsertionPointLocator::AccessSpec xsSpec)
78 {
79     static QList<InsertionPointLocator::AccessSpec> order = QList<InsertionPointLocator::AccessSpec>()
80             << InsertionPointLocator::Public
81             << InsertionPointLocator::PublicSlot
82             << InsertionPointLocator::Signals
83             << InsertionPointLocator::Protected
84             << InsertionPointLocator::ProtectedSlot
85             << InsertionPointLocator::PrivateSlot
86             << InsertionPointLocator::Private
87             ;
88
89     return order.indexOf(xsSpec);
90 }
91
92 struct AccessRange
93 {
94     unsigned start;
95     unsigned end;
96     InsertionPointLocator::AccessSpec xsSpec;
97
98     AccessRange()
99         : start(0)
100         , end(0)
101         , xsSpec(InsertionPointLocator::Invalid)
102     {}
103
104     AccessRange(unsigned start, unsigned end, InsertionPointLocator::AccessSpec xsSpec)
105         : start(start)
106         , end(end)
107         , xsSpec(xsSpec)
108     {}
109 };
110
111 class FindInClass: public ASTVisitor
112 {
113 public:
114     FindInClass(const Document::Ptr &doc, const Class *clazz, InsertionPointLocator::AccessSpec xsSpec)
115         : ASTVisitor(doc->translationUnit())
116         , _doc(doc)
117         , _clazz(clazz)
118         , _xsSpec(xsSpec)
119     {}
120
121     InsertionLocation operator()()
122     {
123         _result = InsertionLocation();
124
125         AST *ast = translationUnit()->ast();
126         accept(ast);
127
128         return _result;
129     }
130
131 protected:
132     using ASTVisitor::visit;
133
134     bool visit(ClassSpecifierAST *ast)
135     {
136         if (!ast->lbrace_token || !ast->rbrace_token)
137             return true;
138         if (!ast->symbol || !ast->symbol->isEqualTo(_clazz))
139             return true;
140
141         QList<AccessRange> ranges = collectAccessRanges(
142                     ast->member_specifier_list,
143                     tokenKind(ast->classkey_token) == T_CLASS ? InsertionPointLocator::Private : InsertionPointLocator::Public,
144                     ast->lbrace_token,
145                     ast->rbrace_token);
146
147         unsigned beforeToken = 0;
148         bool needsPrefix = false;
149         bool needsSuffix = false;
150         findMatch(ranges, _xsSpec, beforeToken, needsPrefix, needsSuffix);
151
152         unsigned line = 0, column = 0;
153         getTokenStartPosition(beforeToken, &line, &column);
154
155         QString prefix;
156         if (needsPrefix)
157             prefix = generate(_xsSpec);
158
159         QString suffix;
160         if (needsSuffix)
161             suffix = QLatin1Char('\n');
162
163         _result = InsertionLocation(_doc->fileName(), prefix, suffix,
164                                     line, column);
165         return false;
166     }
167
168     static void findMatch(const QList<AccessRange> &ranges,
169                           InsertionPointLocator::AccessSpec xsSpec,
170                           unsigned &beforeToken,
171                           bool &needsPrefix,
172                           bool &needsSuffix)
173     {
174         Q_ASSERT(!ranges.isEmpty());
175         const int lastIndex = ranges.size() - 1;
176
177         // try an exact match, and ignore the first (default) access spec:
178         for (int i = lastIndex; i > 0; --i) {
179             const AccessRange &range = ranges.at(i);
180             if (range.xsSpec == xsSpec) {
181                 beforeToken = range.end;
182                 needsPrefix = false;
183                 needsSuffix = (i != lastIndex);
184                 return;
185             }
186         }
187
188         // try to find a fitting access spec to insert XXX:
189         for (int i = lastIndex; i > 0; --i) {
190             const AccessRange &current = ranges.at(i);
191
192             if (ordering(xsSpec) > ordering(current.xsSpec)) {
193                 beforeToken = current.end;
194                 needsPrefix = true;
195                 needsSuffix = (i != lastIndex);
196                 return;
197             }
198         }
199
200         // otherwise:
201         beforeToken = ranges.first().end;
202         needsPrefix = true;
203         needsSuffix = (ranges.size() != 1);
204     }
205
206     QList<AccessRange> collectAccessRanges(DeclarationListAST *decls,
207                                            InsertionPointLocator::AccessSpec initialXs,
208                                            int firstRangeStart,
209                                            int lastRangeEnd) const
210     {
211         QList<AccessRange> ranges;
212         ranges.append(AccessRange(firstRangeStart, lastRangeEnd, initialXs));
213
214         for (DeclarationListAST *iter = decls; iter; iter = iter->next) {
215             DeclarationAST *decl = iter->value;
216
217             if (AccessDeclarationAST *xsDecl = decl->asAccessDeclaration()) {
218                 const unsigned token = xsDecl->access_specifier_token;
219                 int newXsSpec = initialXs;
220                 bool isSlot = xsDecl->slots_token
221                         && tokenKind(xsDecl->slots_token) == T_Q_SLOTS;
222
223                 switch (tokenKind(token)) {
224                 case T_PUBLIC:
225                     newXsSpec = isSlot ? InsertionPointLocator::PublicSlot
226                                        : InsertionPointLocator::Public;
227                     break;
228
229                 case T_PROTECTED:
230                     newXsSpec = isSlot ? InsertionPointLocator::ProtectedSlot
231                                        : InsertionPointLocator::Protected;
232                     break;
233
234                 case T_PRIVATE:
235                     newXsSpec = isSlot ? InsertionPointLocator::PrivateSlot
236                                        : InsertionPointLocator::Private;
237                     break;
238
239                 case T_Q_SIGNALS:
240                     newXsSpec = InsertionPointLocator::Signals;
241                     break;
242
243                 case T_Q_SLOTS: {
244                     newXsSpec = ranges.last().xsSpec | InsertionPointLocator::SlotBit;
245                     break;
246                 }
247
248                 default:
249                     break;
250                 }
251
252                 if (newXsSpec != ranges.last().xsSpec) {
253                     ranges.last().end = token;
254                     ranges.append(AccessRange(token, lastRangeEnd, (InsertionPointLocator::AccessSpec) newXsSpec));
255                 }
256             }
257         }
258
259         ranges.last().end = lastRangeEnd;
260         return ranges;
261     }
262
263 private:
264     Document::Ptr _doc;
265     const Class *_clazz;
266     InsertionPointLocator::AccessSpec _xsSpec;
267
268     InsertionLocation _result;
269 };
270
271 } // end of anonymous namespace
272
273 InsertionLocation::InsertionLocation()
274     : m_line(0)
275     , m_column(0)
276 {}
277
278 InsertionLocation::InsertionLocation(const QString &fileName,
279                                      const QString &prefix,
280                                      const QString &suffix,
281                                      unsigned line, unsigned column)
282     : m_fileName(fileName)
283     , m_prefix(prefix)
284     , m_suffix(suffix)
285     , m_line(line)
286     , m_column(column)
287 {}
288
289 InsertionPointLocator::InsertionPointLocator(CppRefactoringChanges *refactoringChanges)
290     : m_refactoringChanges(refactoringChanges)
291 {
292 }
293
294 InsertionLocation InsertionPointLocator::methodDeclarationInClass(
295     const QString &fileName,
296     const Class *clazz,
297     AccessSpec xsSpec) const
298 {
299     const Document::Ptr doc = m_refactoringChanges->file(fileName).cppDocument();
300     if (doc) {
301         FindInClass find(doc, clazz, xsSpec);
302         return find();
303     } else {
304         return InsertionLocation();
305     }
306 }
307
308 static bool isSourceFile(const QString &fileName)
309 {
310     const Core::MimeDatabase *mimeDb = Core::ICore::instance()->mimeDatabase();
311     Core::MimeType cSourceTy = mimeDb->findByType(QLatin1String("text/x-csrc"));
312     Core::MimeType cppSourceTy = mimeDb->findByType(QLatin1String("text/x-c++src"));
313     Core::MimeType mSourceTy = mimeDb->findByType(QLatin1String("text/x-objcsrc"));
314     QStringList suffixes = cSourceTy.suffixes();
315     suffixes += cppSourceTy.suffixes();
316     suffixes += mSourceTy.suffixes();
317     QFileInfo fileInfo(fileName);
318     return suffixes.contains(fileInfo.suffix());
319 }
320
321 /// Currently, we return the end of fileName.cpp
322 /// \todo take the definitions of the surrounding declarations into account
323 QList<InsertionLocation> InsertionPointLocator::methodDefinition(
324     Declaration *declaration) const
325 {
326     QList<InsertionLocation> result;
327     if (!declaration)
328         return result;
329
330     const QString declFileName = QString::fromUtf8(declaration->fileName(),
331                                                    declaration->fileNameLength());
332     QString target = declFileName;
333     if (!isSourceFile(declFileName)) {
334         Internal::CppToolsPlugin *cpptools = Internal::CppToolsPlugin::instance();
335         QString candidate = cpptools->correspondingHeaderOrSource(declFileName);
336         if (!candidate.isEmpty())
337             target = candidate;
338     }
339
340     Document::Ptr doc = m_refactoringChanges->file(target).cppDocument();
341     if (doc.isNull())
342         return result;
343
344     Snapshot simplified = m_refactoringChanges->snapshot().simplified(doc);
345     if (Symbol *s = simplified.findMatchingDefinition(declaration)) {
346         if (Function *f = s->asFunction()) {
347             if (f->isConst() == declaration->type().isConst()
348                     && f->isVolatile() == declaration->type().isVolatile())
349                 return result;
350         }
351     }
352
353     TranslationUnit *xUnit = doc->translationUnit();
354     unsigned tokenCount = xUnit->tokenCount();
355     if (tokenCount < 2) // no tokens available
356         return result;
357
358     unsigned line = 0, column = 0;
359     xUnit->getTokenEndPosition(xUnit->tokenCount() - 2, &line, &column);
360
361     const QLatin1String prefix("\n\n");
362     result.append(InsertionLocation(target, prefix, QString(), line, column));
363
364     return result;
365 }