1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
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.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "cpptoolsplugin.h"
34 #include "cpprefactoringchanges.h"
35 #include "insertionpointlocator.h"
38 #include <ASTVisitor.h>
39 #include <TranslationUnit.h>
41 #include <coreplugin/icore.h>
42 #include <coreplugin/mimedatabase.h>
44 using namespace CPlusPlus;
45 using namespace CppTools;
49 static QString generate(InsertionPointLocator::AccessSpec xsSpec)
53 case InsertionPointLocator::Public:
54 return QLatin1String("public:\n");
56 case InsertionPointLocator::Protected:
57 return QLatin1String("protected:\n");
59 case InsertionPointLocator::Private:
60 return QLatin1String("private:\n");
62 case InsertionPointLocator::PublicSlot:
63 return QLatin1String("public slots:\n");
65 case InsertionPointLocator::ProtectedSlot:
66 return QLatin1String("protected slots:\n");
68 case InsertionPointLocator::PrivateSlot:
69 return QLatin1String("private slots:\n");
71 case InsertionPointLocator::Signals:
72 return QLatin1String("signals:\n");
76 static int ordering(InsertionPointLocator::AccessSpec xsSpec)
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
88 return order.indexOf(xsSpec);
95 InsertionPointLocator::AccessSpec xsSpec;
100 , xsSpec(InsertionPointLocator::Invalid)
103 AccessRange(unsigned start, unsigned end, InsertionPointLocator::AccessSpec xsSpec)
110 class FindInClass: public ASTVisitor
113 FindInClass(const Document::Ptr &doc, const Class *clazz, InsertionPointLocator::AccessSpec xsSpec)
114 : ASTVisitor(doc->translationUnit())
120 InsertionLocation operator()()
122 _result = InsertionLocation();
124 AST *ast = translationUnit()->ast();
131 using ASTVisitor::visit;
133 bool visit(ClassSpecifierAST *ast)
135 if (!ast->lbrace_token || !ast->rbrace_token)
137 if (!ast->symbol || !ast->symbol->isEqualTo(_clazz))
140 QList<AccessRange> ranges = collectAccessRanges(
141 ast->member_specifier_list,
142 tokenKind(ast->classkey_token) == T_CLASS ? InsertionPointLocator::Private : InsertionPointLocator::Public,
146 unsigned beforeToken = 0;
147 bool needsPrefix = false;
148 bool needsSuffix = false;
149 findMatch(ranges, _xsSpec, beforeToken, needsPrefix, needsSuffix);
151 unsigned line = 0, column = 0;
152 getTokenStartPosition(beforeToken, &line, &column);
156 prefix = generate(_xsSpec);
160 suffix = QLatin1Char('\n');
162 _result = InsertionLocation(_doc->fileName(), prefix, suffix,
167 static void findMatch(const QList<AccessRange> &ranges,
168 InsertionPointLocator::AccessSpec xsSpec,
169 unsigned &beforeToken,
173 Q_ASSERT(!ranges.isEmpty());
174 const int lastIndex = ranges.size() - 1;
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;
182 needsSuffix = (i != lastIndex);
187 // try to find a fitting access spec to insert XXX:
188 for (int i = lastIndex; i > 0; --i) {
189 const AccessRange ¤t = ranges.at(i);
191 if (ordering(xsSpec) > ordering(current.xsSpec)) {
192 beforeToken = current.end;
194 needsSuffix = (i != lastIndex);
200 beforeToken = ranges.first().end;
202 needsSuffix = (ranges.size() != 1);
205 QList<AccessRange> collectAccessRanges(DeclarationListAST *decls,
206 InsertionPointLocator::AccessSpec initialXs,
208 int lastRangeEnd) const
210 QList<AccessRange> ranges;
211 ranges.append(AccessRange(firstRangeStart, lastRangeEnd, initialXs));
213 for (DeclarationListAST *iter = decls; iter; iter = iter->next) {
214 DeclarationAST *decl = iter->value;
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;
222 switch (tokenKind(token)) {
224 newXsSpec = isSlot ? InsertionPointLocator::PublicSlot
225 : InsertionPointLocator::Public;
229 newXsSpec = isSlot ? InsertionPointLocator::ProtectedSlot
230 : InsertionPointLocator::Protected;
234 newXsSpec = isSlot ? InsertionPointLocator::PrivateSlot
235 : InsertionPointLocator::Private;
239 newXsSpec = InsertionPointLocator::Signals;
243 newXsSpec = ranges.last().xsSpec | InsertionPointLocator::SlotBit;
251 if (newXsSpec != ranges.last().xsSpec) {
252 ranges.last().end = token;
253 ranges.append(AccessRange(token, lastRangeEnd, (InsertionPointLocator::AccessSpec) newXsSpec));
258 ranges.last().end = lastRangeEnd;
265 InsertionPointLocator::AccessSpec _xsSpec;
267 InsertionLocation _result;
270 } // end of anonymous namespace
272 InsertionLocation::InsertionLocation()
277 InsertionLocation::InsertionLocation(const QString &fileName,
278 const QString &prefix,
279 const QString &suffix,
280 unsigned line, unsigned column)
281 : m_fileName(fileName)
288 InsertionPointLocator::InsertionPointLocator(CppRefactoringChanges *refactoringChanges)
289 : m_refactoringChanges(refactoringChanges)
293 InsertionLocation InsertionPointLocator::methodDeclarationInClass(
294 const QString &fileName,
296 AccessSpec xsSpec) const
298 const Document::Ptr doc = m_refactoringChanges->file(fileName).cppDocument();
300 FindInClass find(doc, clazz, xsSpec);
303 return InsertionLocation();
307 static bool isSourceFile(const QString &fileName)
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());
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
325 QList<InsertionLocation> result;
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())
339 Document::Ptr doc = m_refactoringChanges->file(target).cppDocument();
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())
352 TranslationUnit *xUnit = doc->translationUnit();
353 unsigned tokenCount = xUnit->tokenCount();
354 if (tokenCount < 2) // no tokens available
357 unsigned line = 0, column = 0;
358 xUnit->getTokenEndPosition(xUnit->tokenCount() - 2, &line, &column);
360 const QLatin1String prefix("\n\n");
361 result.append(InsertionLocation(target, prefix, QString(), line, column));