OSDN Git Service

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