| 
									
										
										
										
											2012-10-02 09:12:39 +02:00
										 |  |  | /****************************************************************************
 | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | ** | 
					
						
							| 
									
										
										
										
											2012-10-02 09:12:39 +02:00
										 |  |  | ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). | 
					
						
							|  |  |  | ** Contact: http://www.qt-project.org/legal
 | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | ** | 
					
						
							| 
									
										
										
										
											2012-10-02 09:12:39 +02:00
										 |  |  | ** This file is part of Qt Creator. | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | ** | 
					
						
							| 
									
										
										
										
											2012-10-02 09:12:39 +02:00
										 |  |  | ** Commercial License Usage | 
					
						
							|  |  |  | ** Licensees holding valid commercial Qt licenses may use this file in | 
					
						
							|  |  |  | ** accordance with the commercial license agreement provided with the | 
					
						
							|  |  |  | ** Software or, alternatively, in accordance with the terms contained in | 
					
						
							|  |  |  | ** a written agreement between you and Digia.  For licensing terms and | 
					
						
							|  |  |  | ** conditions see http://qt.digia.com/licensing.  For further information
 | 
					
						
							|  |  |  | ** use the contact form at http://qt.digia.com/contact-us.
 | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | ** | 
					
						
							|  |  |  | ** GNU Lesser General Public License Usage | 
					
						
							| 
									
										
										
										
											2012-10-02 09:12:39 +02:00
										 |  |  | ** 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.
 | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | ** | 
					
						
							| 
									
										
										
										
											2012-10-02 09:12:39 +02:00
										 |  |  | ** In addition, as a special exception, Digia gives you certain additional | 
					
						
							|  |  |  | ** rights.  These rights are described in the Digia Qt LGPL Exception | 
					
						
							| 
									
										
										
										
											2010-12-17 16:01:08 +01:00
										 |  |  | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | 
					
						
							|  |  |  | ** | 
					
						
							| 
									
										
										
										
											2012-10-02 09:12:39 +02:00
										 |  |  | ****************************************************************************/ | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-10 21:00:48 +02:00
										 |  |  | #include "cpptoolsreuse.h"
 | 
					
						
							| 
									
										
										
										
											2010-09-30 12:09:38 +02:00
										 |  |  | #include "cpprefactoringchanges.h"
 | 
					
						
							| 
									
										
										
										
											2010-09-30 14:00:43 +02:00
										 |  |  | #include "insertionpointlocator.h"
 | 
					
						
							| 
									
										
										
										
											2012-01-20 14:43:21 +01:00
										 |  |  | #include "symbolfinder.h"
 | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <AST.h>
 | 
					
						
							|  |  |  | #include <ASTVisitor.h>
 | 
					
						
							|  |  |  | #include <TranslationUnit.h>
 | 
					
						
							| 
									
										
										
										
											2011-07-28 22:11:38 +02:00
										 |  |  | #include <Literals.h>
 | 
					
						
							| 
									
										
										
										
											2011-08-18 15:04:13 +02:00
										 |  |  | #include <Symbols.h>
 | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-06 14:01:59 +02:00
										 |  |  | #include <coreplugin/icore.h>
 | 
					
						
							|  |  |  | #include <coreplugin/mimedatabase.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-23 17:44:49 +01:00
										 |  |  | #include <utils/qtcassert.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | using namespace CPlusPlus; | 
					
						
							| 
									
										
										
										
											2010-09-30 12:09:38 +02:00
										 |  |  | using namespace CppTools; | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static QString generate(InsertionPointLocator::AccessSpec xsSpec) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     switch (xsSpec) { | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |     case InsertionPointLocator::Public: | 
					
						
							|  |  |  |         return QLatin1String("public:\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case InsertionPointLocator::Protected: | 
					
						
							|  |  |  |         return QLatin1String("protected:\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case InsertionPointLocator::Private: | 
					
						
							|  |  |  |         return QLatin1String("private:\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case InsertionPointLocator::PublicSlot: | 
					
						
							|  |  |  |         return QLatin1String("public slots:\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case InsertionPointLocator::ProtectedSlot: | 
					
						
							|  |  |  |         return QLatin1String("protected slots:\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case InsertionPointLocator::PrivateSlot: | 
					
						
							| 
									
										
										
										
											2010-07-27 17:02:12 +02:00
										 |  |  |         return QLatin1String("private slots:\n"); | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     case InsertionPointLocator::Signals: | 
					
						
							|  |  |  |         return QLatin1String("signals:\n"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  | static int ordering(InsertionPointLocator::AccessSpec xsSpec) | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |     static QList<InsertionPointLocator::AccessSpec> order = QList<InsertionPointLocator::AccessSpec>() | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |             << InsertionPointLocator::Public | 
					
						
							|  |  |  |             << InsertionPointLocator::PublicSlot | 
					
						
							|  |  |  |             << InsertionPointLocator::Signals | 
					
						
							|  |  |  |             << InsertionPointLocator::Protected | 
					
						
							|  |  |  |             << InsertionPointLocator::ProtectedSlot | 
					
						
							|  |  |  |             << InsertionPointLocator::PrivateSlot | 
					
						
							|  |  |  |             << InsertionPointLocator::Private | 
					
						
							|  |  |  |             ; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |     return order.indexOf(xsSpec); | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct AccessRange | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     unsigned start; | 
					
						
							|  |  |  |     unsigned end; | 
					
						
							|  |  |  |     InsertionPointLocator::AccessSpec xsSpec; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     AccessRange() | 
					
						
							|  |  |  |         : start(0) | 
					
						
							|  |  |  |         , end(0) | 
					
						
							|  |  |  |         , xsSpec(InsertionPointLocator::Invalid) | 
					
						
							|  |  |  |     {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     AccessRange(unsigned start, unsigned end, InsertionPointLocator::AccessSpec xsSpec) | 
					
						
							|  |  |  |         : start(start) | 
					
						
							|  |  |  |         , end(end) | 
					
						
							|  |  |  |         , xsSpec(xsSpec) | 
					
						
							|  |  |  |     {} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FindInClass: public ASTVisitor | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     FindInClass(const Document::Ptr &doc, const Class *clazz, InsertionPointLocator::AccessSpec xsSpec) | 
					
						
							|  |  |  |         : ASTVisitor(doc->translationUnit()) | 
					
						
							|  |  |  |         , _doc(doc) | 
					
						
							|  |  |  |         , _clazz(clazz) | 
					
						
							|  |  |  |         , _xsSpec(xsSpec) | 
					
						
							|  |  |  |     {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     InsertionLocation operator()() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         _result = InsertionLocation(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         AST *ast = translationUnit()->ast(); | 
					
						
							|  |  |  |         accept(ast); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return _result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | protected: | 
					
						
							|  |  |  |     using ASTVisitor::visit; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool visit(ClassSpecifierAST *ast) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!ast->lbrace_token || !ast->rbrace_token) | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         if (!ast->symbol || !ast->symbol->isEqualTo(_clazz)) | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         QList<AccessRange> ranges = collectAccessRanges( | 
					
						
							|  |  |  |                     ast->member_specifier_list, | 
					
						
							|  |  |  |                     tokenKind(ast->classkey_token) == T_CLASS ? InsertionPointLocator::Private : InsertionPointLocator::Public, | 
					
						
							|  |  |  |                     ast->lbrace_token, | 
					
						
							|  |  |  |                     ast->rbrace_token); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |         unsigned beforeToken = 0; | 
					
						
							|  |  |  |         bool needsPrefix = false; | 
					
						
							|  |  |  |         bool needsSuffix = false; | 
					
						
							|  |  |  |         findMatch(ranges, _xsSpec, beforeToken, needsPrefix, needsSuffix); | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         unsigned line = 0, column = 0; | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |         getTokenStartPosition(beforeToken, &line, &column); | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         QString prefix; | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |         if (needsPrefix) | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |             prefix = generate(_xsSpec); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |         QString suffix; | 
					
						
							|  |  |  |         if (needsSuffix) | 
					
						
							|  |  |  |             suffix = QLatin1Char('\n'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-30 12:09:38 +02:00
										 |  |  |         _result = InsertionLocation(_doc->fileName(), prefix, suffix, | 
					
						
							|  |  |  |                                     line, column); | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |     static void findMatch(const QList<AccessRange> &ranges, | 
					
						
							|  |  |  |                           InsertionPointLocator::AccessSpec xsSpec, | 
					
						
							|  |  |  |                           unsigned &beforeToken, | 
					
						
							|  |  |  |                           bool &needsPrefix, | 
					
						
							|  |  |  |                           bool &needsSuffix) | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2012-01-23 17:44:49 +01:00
										 |  |  |         QTC_ASSERT(!ranges.isEmpty(), return); | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |         const int lastIndex = ranges.size() - 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |         // try an exact match, and ignore the first (default) access spec:
 | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |         for (int i = lastIndex; i > 0; --i) { | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |             const AccessRange &range = ranges.at(i); | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |             if (range.xsSpec == xsSpec) { | 
					
						
							|  |  |  |                 beforeToken = range.end; | 
					
						
							|  |  |  |                 needsPrefix = false; | 
					
						
							|  |  |  |                 needsSuffix = (i != lastIndex); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |         // try to find a fitting access spec to insert XXX:
 | 
					
						
							|  |  |  |         for (int i = lastIndex; i > 0; --i) { | 
					
						
							|  |  |  |             const AccessRange ¤t = ranges.at(i); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (ordering(xsSpec) > ordering(current.xsSpec)) { | 
					
						
							|  |  |  |                 beforeToken = current.end; | 
					
						
							|  |  |  |                 needsPrefix = true; | 
					
						
							|  |  |  |                 needsSuffix = (i != lastIndex); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // otherwise:
 | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |         beforeToken = ranges.first().end; | 
					
						
							|  |  |  |         needsPrefix = true; | 
					
						
							|  |  |  |         needsSuffix = (ranges.size() != 1); | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QList<AccessRange> collectAccessRanges(DeclarationListAST *decls, | 
					
						
							|  |  |  |                                            InsertionPointLocator::AccessSpec initialXs, | 
					
						
							|  |  |  |                                            int firstRangeStart, | 
					
						
							|  |  |  |                                            int lastRangeEnd) const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         QList<AccessRange> ranges; | 
					
						
							|  |  |  |         ranges.append(AccessRange(firstRangeStart, lastRangeEnd, initialXs)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (DeclarationListAST *iter = decls; iter; iter = iter->next) { | 
					
						
							|  |  |  |             DeclarationAST *decl = iter->value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (AccessDeclarationAST *xsDecl = decl->asAccessDeclaration()) { | 
					
						
							|  |  |  |                 const unsigned token = xsDecl->access_specifier_token; | 
					
						
							|  |  |  |                 int newXsSpec = initialXs; | 
					
						
							| 
									
										
										
										
											2010-09-27 18:01:04 +02:00
										 |  |  |                 bool isSlot = xsDecl->slots_token | 
					
						
							|  |  |  |                         && tokenKind(xsDecl->slots_token) == T_Q_SLOTS; | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 switch (tokenKind(token)) { | 
					
						
							|  |  |  |                 case T_PUBLIC: | 
					
						
							| 
									
										
										
										
											2010-09-27 18:01:04 +02:00
										 |  |  |                     newXsSpec = isSlot ? InsertionPointLocator::PublicSlot | 
					
						
							|  |  |  |                                        : InsertionPointLocator::Public; | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |                     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 case T_PROTECTED: | 
					
						
							| 
									
										
										
										
											2010-09-27 18:01:04 +02:00
										 |  |  |                     newXsSpec = isSlot ? InsertionPointLocator::ProtectedSlot | 
					
						
							|  |  |  |                                        : InsertionPointLocator::Protected; | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |                     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 case T_PRIVATE: | 
					
						
							| 
									
										
										
										
											2010-09-27 18:01:04 +02:00
										 |  |  |                     newXsSpec = isSlot ? InsertionPointLocator::PrivateSlot | 
					
						
							|  |  |  |                                        : InsertionPointLocator::Private; | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |                     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 case T_Q_SIGNALS: | 
					
						
							|  |  |  |                     newXsSpec = InsertionPointLocator::Signals; | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 case T_Q_SLOTS: { | 
					
						
							|  |  |  |                     newXsSpec = ranges.last().xsSpec | InsertionPointLocator::SlotBit; | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 default: | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (newXsSpec != ranges.last().xsSpec) { | 
					
						
							|  |  |  |                     ranges.last().end = token; | 
					
						
							|  |  |  |                     ranges.append(AccessRange(token, lastRangeEnd, (InsertionPointLocator::AccessSpec) newXsSpec)); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ranges.last().end = lastRangeEnd; | 
					
						
							|  |  |  |         return ranges; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |     Document::Ptr _doc; | 
					
						
							|  |  |  |     const Class *_clazz; | 
					
						
							|  |  |  |     InsertionPointLocator::AccessSpec _xsSpec; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     InsertionLocation _result; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // end of anonymous namespace
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | InsertionLocation::InsertionLocation() | 
					
						
							|  |  |  |     : m_line(0) | 
					
						
							|  |  |  |     , m_column(0) | 
					
						
							|  |  |  | {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-30 12:09:38 +02:00
										 |  |  | InsertionLocation::InsertionLocation(const QString &fileName, | 
					
						
							|  |  |  |                                      const QString &prefix, | 
					
						
							|  |  |  |                                      const QString &suffix, | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |                                      unsigned line, unsigned column) | 
					
						
							| 
									
										
										
										
											2010-09-30 12:09:38 +02:00
										 |  |  |     : m_fileName(fileName) | 
					
						
							|  |  |  |     , m_prefix(prefix) | 
					
						
							| 
									
										
										
										
											2010-07-28 12:09:50 +02:00
										 |  |  |     , m_suffix(suffix) | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  |     , m_line(line) | 
					
						
							|  |  |  |     , m_column(column) | 
					
						
							|  |  |  | {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-17 11:35:57 +02:00
										 |  |  | InsertionPointLocator::InsertionPointLocator(const CppRefactoringChanges &refactoringChanges) | 
					
						
							| 
									
										
										
										
											2010-09-30 12:09:38 +02:00
										 |  |  |     : m_refactoringChanges(refactoringChanges) | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-27 18:01:04 +02:00
										 |  |  | InsertionLocation InsertionPointLocator::methodDeclarationInClass( | 
					
						
							|  |  |  |     const QString &fileName, | 
					
						
							|  |  |  |     const Class *clazz, | 
					
						
							|  |  |  |     AccessSpec xsSpec) const | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-08-17 11:35:57 +02:00
										 |  |  |     const Document::Ptr doc = m_refactoringChanges.file(fileName)->cppDocument(); | 
					
						
							| 
									
										
										
										
											2010-09-27 18:01:04 +02:00
										 |  |  |     if (doc) { | 
					
						
							|  |  |  |         FindInClass find(doc, clazz, xsSpec); | 
					
						
							|  |  |  |         return find(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         return InsertionLocation(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-06 14:01:59 +02:00
										 |  |  | static bool isSourceFile(const QString &fileName) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-01-24 15:36:40 +01:00
										 |  |  |     const Core::MimeDatabase *mimeDb = Core::ICore::mimeDatabase(); | 
					
						
							| 
									
										
										
										
											2010-10-06 14:01:59 +02:00
										 |  |  |     Core::MimeType cSourceTy = mimeDb->findByType(QLatin1String("text/x-csrc")); | 
					
						
							|  |  |  |     Core::MimeType cppSourceTy = mimeDb->findByType(QLatin1String("text/x-c++src")); | 
					
						
							|  |  |  |     Core::MimeType mSourceTy = mimeDb->findByType(QLatin1String("text/x-objcsrc")); | 
					
						
							|  |  |  |     QStringList suffixes = cSourceTy.suffixes(); | 
					
						
							|  |  |  |     suffixes += cppSourceTy.suffixes(); | 
					
						
							|  |  |  |     suffixes += mSourceTy.suffixes(); | 
					
						
							|  |  |  |     QFileInfo fileInfo(fileName); | 
					
						
							|  |  |  |     return suffixes.contains(fileInfo.suffix()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-28 22:11:38 +02:00
										 |  |  | namespace { | 
					
						
							|  |  |  | template <class Key, class Value> | 
					
						
							|  |  |  | class HighestValue | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Key _key; | 
					
						
							|  |  |  |     Value _value; | 
					
						
							|  |  |  |     bool _set; | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     HighestValue() | 
					
						
							| 
									
										
										
										
											2012-01-31 13:41:39 +01:00
										 |  |  |         : _key(), _set(false) | 
					
						
							| 
									
										
										
										
											2011-07-28 22:11:38 +02:00
										 |  |  |     {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     HighestValue(const Key &initialKey, const Value &initialValue) | 
					
						
							|  |  |  |         : _key(initialKey) | 
					
						
							|  |  |  |         , _value(initialValue) | 
					
						
							|  |  |  |         , _set(true) | 
					
						
							|  |  |  |     {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void maybeSet(const Key &key, const Value &value) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!_set || key > _key) { | 
					
						
							|  |  |  |             _value = value; | 
					
						
							|  |  |  |             _key = key; | 
					
						
							|  |  |  |             _set = true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const Value &get() const | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2012-01-24 13:50:35 +01:00
										 |  |  |         QTC_CHECK(_set); | 
					
						
							| 
									
										
										
										
											2011-07-28 22:11:38 +02:00
										 |  |  |         return _value; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FindMethodDefinitionInsertPoint : protected ASTVisitor | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QList<const Identifier *> _namespaceNames; | 
					
						
							|  |  |  |     int _currentDepth; | 
					
						
							|  |  |  |     HighestValue<int, int> _bestToken; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     FindMethodDefinitionInsertPoint(TranslationUnit *translationUnit) | 
					
						
							|  |  |  |         : ASTVisitor(translationUnit) | 
					
						
							|  |  |  |     {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void operator()(Declaration *decl, unsigned *line, unsigned *column) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // default to end of file
 | 
					
						
							| 
									
										
										
										
											2012-01-31 13:41:39 +01:00
										 |  |  |         _bestToken.maybeSet(-1, translationUnit()->ast()->lastToken()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (translationUnit()->ast()->lastToken() >= 2) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             QList<const Name *> names = LookupContext::fullyQualifiedName(decl); | 
					
						
							|  |  |  |             foreach (const Name *name, names) { | 
					
						
							|  |  |  |                 const Identifier *id = name->asNameId(); | 
					
						
							|  |  |  |                 if (!id) | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 _namespaceNames += id; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             _currentDepth = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             accept(translationUnit()->ast()); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2011-07-28 22:11:38 +02:00
										 |  |  |         translationUnit()->getTokenEndPosition(_bestToken.get(), line, column); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | protected: | 
					
						
							|  |  |  |     bool preVisit(AST *ast) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return ast->asNamespace() || ast->asTranslationUnit() || ast->asLinkageBody(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool visit(NamespaceAST *ast) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (_currentDepth >= _namespaceNames.size()) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // ignore anonymous namespaces
 | 
					
						
							|  |  |  |         if (!ast->identifier_token) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const Identifier *name = translationUnit()->identifier(ast->identifier_token); | 
					
						
							|  |  |  |         if (!name->equalTo(_namespaceNames.at(_currentDepth))) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // found a good namespace
 | 
					
						
							|  |  |  |         _bestToken.maybeSet(_currentDepth, ast->lastToken() - 2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ++_currentDepth; | 
					
						
							|  |  |  |         accept(ast->linkage_body); | 
					
						
							|  |  |  |         --_currentDepth; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2011-08-29 09:53:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class FindFunctionDefinition : protected ASTVisitor | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     FunctionDefinitionAST *_result; | 
					
						
							|  |  |  |     unsigned _line, _column; | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     FindFunctionDefinition(TranslationUnit *translationUnit) | 
					
						
							|  |  |  |         : ASTVisitor(translationUnit) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     FunctionDefinitionAST *operator()(unsigned line, unsigned column) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         _result = 0; | 
					
						
							|  |  |  |         _line = line; | 
					
						
							|  |  |  |         _column = column; | 
					
						
							|  |  |  |         accept(translationUnit()->ast()); | 
					
						
							|  |  |  |         return _result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | protected: | 
					
						
							|  |  |  |     bool preVisit(AST *ast) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (_result) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         unsigned line, column; | 
					
						
							|  |  |  |         translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column); | 
					
						
							|  |  |  |         if (line > _line || (line == _line && column > _column)) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         translationUnit()->getTokenEndPosition(ast->lastToken() - 1, &line, &column); | 
					
						
							|  |  |  |         if (line < _line || (line == _line && column < _column)) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool visit(FunctionDefinitionAST *ast) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         _result = ast; | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-28 22:11:38 +02:00
										 |  |  | } // anonymous namespace
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-29 09:53:52 +02:00
										 |  |  | static Declaration *isNonVirtualFunctionDeclaration(Symbol *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!s) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     Declaration *declaration = s->asDeclaration(); | 
					
						
							|  |  |  |     if (!declaration) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     Function *type = s->type()->asFunctionType(); | 
					
						
							|  |  |  |     if (!type || type->isPureVirtual()) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     return declaration; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static InsertionLocation nextToSurroundingDefinitions(Declaration *declaration, const CppRefactoringChanges &changes) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     InsertionLocation noResult; | 
					
						
							|  |  |  |     Class *klass = declaration->enclosingClass(); | 
					
						
							|  |  |  |     if (!klass) | 
					
						
							|  |  |  |         return noResult; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // find the index of declaration
 | 
					
						
							|  |  |  |     int declIndex = -1; | 
					
						
							|  |  |  |     for (unsigned i = 0; i < klass->memberCount(); ++i) { | 
					
						
							|  |  |  |         Symbol *s = klass->memberAt(i); | 
					
						
							|  |  |  |         if (s == declaration) { | 
					
						
							|  |  |  |             declIndex = i; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (declIndex == -1) | 
					
						
							|  |  |  |         return noResult; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // scan preceding declarations for a function declaration
 | 
					
						
							|  |  |  |     QString prefix, suffix; | 
					
						
							|  |  |  |     Declaration *surroundingFunctionDecl = 0; | 
					
						
							|  |  |  |     for (int i = declIndex - 1; i >= 0; --i) { | 
					
						
							|  |  |  |         Symbol *s = klass->memberAt(i); | 
					
						
							|  |  |  |         surroundingFunctionDecl = isNonVirtualFunctionDeclaration(s); | 
					
						
							|  |  |  |         if (surroundingFunctionDecl) { | 
					
						
							|  |  |  |             prefix = QLatin1String("\n\n"); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!surroundingFunctionDecl) { | 
					
						
							|  |  |  |         // try to find one below
 | 
					
						
							|  |  |  |         for (unsigned i = declIndex + 1; i < klass->memberCount(); ++i) { | 
					
						
							|  |  |  |             Symbol *s = klass->memberAt(i); | 
					
						
							|  |  |  |             surroundingFunctionDecl = isNonVirtualFunctionDeclaration(s); | 
					
						
							|  |  |  |             if (surroundingFunctionDecl) { | 
					
						
							|  |  |  |                 suffix = QLatin1String("\n\n"); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (!surroundingFunctionDecl) | 
					
						
							|  |  |  |             return noResult; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // find the declaration's definition
 | 
					
						
							| 
									
										
										
										
											2012-01-23 16:08:01 +01:00
										 |  |  |     CppTools::SymbolFinder symbolFinder; | 
					
						
							| 
									
										
										
										
											2012-01-20 14:43:21 +01:00
										 |  |  |     Symbol *definition = symbolFinder.findMatchingDefinition(surroundingFunctionDecl, | 
					
						
							|  |  |  |                                                              changes.snapshot()); | 
					
						
							| 
									
										
										
										
											2011-08-29 09:53:52 +02:00
										 |  |  |     if (!definition) | 
					
						
							|  |  |  |         return noResult; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     unsigned line, column; | 
					
						
							|  |  |  |     if (suffix.isEmpty()) { | 
					
						
							|  |  |  |         Function *definitionFunction = definition->asFunction(); | 
					
						
							|  |  |  |         if (!definitionFunction) | 
					
						
							|  |  |  |             return noResult; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-21 22:36:47 +02:00
										 |  |  |         Document::Ptr targetDoc = changes.snapshot().document(QString::fromUtf8(definition->fileName())); | 
					
						
							| 
									
										
										
										
											2011-08-29 09:53:52 +02:00
										 |  |  |         if (!targetDoc) | 
					
						
							|  |  |  |             return noResult; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         targetDoc->translationUnit()->getPosition(definitionFunction->endOffset(), &line, &column); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         // we don't have an offset to the start of the function definition, so we need to manually find it...
 | 
					
						
							| 
									
										
										
										
											2012-11-21 22:36:47 +02:00
										 |  |  |         CppRefactoringFilePtr targetFile = changes.file(QString::fromUtf8(definition->fileName())); | 
					
						
							| 
									
										
										
										
											2011-08-29 09:53:52 +02:00
										 |  |  |         if (!targetFile->isValid()) | 
					
						
							|  |  |  |             return noResult; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         FindFunctionDefinition finder(targetFile->cppDocument()->translationUnit()); | 
					
						
							|  |  |  |         FunctionDefinitionAST *functionDefinition = finder(definition->line(), definition->column()); | 
					
						
							|  |  |  |         if (!functionDefinition) | 
					
						
							|  |  |  |             return noResult; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         targetFile->cppDocument()->translationUnit()->getTokenStartPosition(functionDefinition->firstToken(), &line, &column); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-21 22:36:47 +02:00
										 |  |  |     return InsertionLocation(QString::fromUtf8(definition->fileName()), prefix, suffix, line, column); | 
					
						
							| 
									
										
										
										
											2011-08-29 09:53:52 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-27 18:01:04 +02:00
										 |  |  | QList<InsertionLocation> InsertionPointLocator::methodDefinition( | 
					
						
							| 
									
										
										
										
											2010-09-30 12:09:38 +02:00
										 |  |  |     Declaration *declaration) const | 
					
						
							| 
									
										
										
										
											2010-09-27 18:01:04 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     QList<InsertionLocation> result; | 
					
						
							| 
									
										
										
										
											2010-09-30 12:09:38 +02:00
										 |  |  |     if (!declaration) | 
					
						
							|  |  |  |         return result; | 
					
						
							| 
									
										
										
										
											2010-09-27 18:01:04 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-23 16:08:01 +01:00
										 |  |  |     CppTools::SymbolFinder symbolFinder; | 
					
						
							| 
									
										
										
										
											2012-01-20 14:43:21 +01:00
										 |  |  |     if (Symbol *s = symbolFinder.findMatchingDefinition(declaration, | 
					
						
							|  |  |  |                                                         m_refactoringChanges.snapshot(), | 
					
						
							|  |  |  |                                                         true)) { | 
					
						
							| 
									
										
										
										
											2011-08-29 09:53:52 +02:00
										 |  |  |         if (Function *f = s->asFunction()) { | 
					
						
							|  |  |  |             if (f->isConst() == declaration->type().isConst() | 
					
						
							|  |  |  |                     && f->isVolatile() == declaration->type().isVolatile()) | 
					
						
							|  |  |  |                 return result; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const InsertionLocation location = nextToSurroundingDefinitions(declaration, m_refactoringChanges); | 
					
						
							|  |  |  |     if (location.isValid()) { | 
					
						
							|  |  |  |         result += location; | 
					
						
							|  |  |  |         return result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-06 14:01:59 +02:00
										 |  |  |     const QString declFileName = QString::fromUtf8(declaration->fileName(), | 
					
						
							|  |  |  |                                                    declaration->fileNameLength()); | 
					
						
							|  |  |  |     QString target = declFileName; | 
					
						
							|  |  |  |     if (!isSourceFile(declFileName)) { | 
					
						
							| 
									
										
										
										
											2012-10-10 21:00:48 +02:00
										 |  |  |         QString candidate = CppTools::correspondingHeaderOrSource(declFileName); | 
					
						
							| 
									
										
										
										
											2010-10-07 15:22:32 +02:00
										 |  |  |         if (!candidate.isEmpty()) | 
					
						
							|  |  |  |             target = candidate; | 
					
						
							| 
									
										
										
										
											2010-10-06 14:01:59 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-09-27 18:01:04 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-31 13:41:39 +01:00
										 |  |  |     CppRefactoringFilePtr targetFile = m_refactoringChanges.file(target); | 
					
						
							|  |  |  |     Document::Ptr doc = targetFile->cppDocument(); | 
					
						
							| 
									
										
										
										
											2010-09-30 12:09:38 +02:00
										 |  |  |     if (doc.isNull()) | 
					
						
							|  |  |  |         return result; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     unsigned line = 0, column = 0; | 
					
						
							| 
									
										
										
										
											2011-07-28 22:11:38 +02:00
										 |  |  |     FindMethodDefinitionInsertPoint finder(doc->translationUnit()); | 
					
						
							|  |  |  |     finder(declaration, &line, &column); | 
					
						
							| 
									
										
										
										
											2010-09-30 12:09:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-12 12:21:44 +02:00
										 |  |  |     // Force empty lines before and after the new definition.
 | 
					
						
							|  |  |  |     QString prefix; | 
					
						
							| 
									
										
										
										
											2012-01-31 13:41:39 +01:00
										 |  |  |     QString suffix; | 
					
						
							| 
									
										
										
										
											2012-09-12 12:21:44 +02:00
										 |  |  |     if (!line) { | 
					
						
							|  |  |  |         // Totally empty file.
 | 
					
						
							|  |  |  |         line = 1; | 
					
						
							|  |  |  |         column = 1; | 
					
						
							|  |  |  |         prefix = suffix = QLatin1Char('\n'); | 
					
						
							| 
									
										
										
										
											2012-01-31 13:41:39 +01:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2012-09-12 12:21:44 +02:00
										 |  |  |         QTC_ASSERT(column, return result); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         prefix = QLatin1String("\n\n"); | 
					
						
							|  |  |  |         int firstNonSpace = targetFile->position(line, column); | 
					
						
							|  |  |  |         QChar c = targetFile->charAt(firstNonSpace); | 
					
						
							|  |  |  |         while (c == QLatin1Char(' ') || c == QLatin1Char('\t')) { | 
					
						
							|  |  |  |             ++firstNonSpace; | 
					
						
							|  |  |  |             c = targetFile->charAt(firstNonSpace); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (targetFile->charAt(firstNonSpace) != QChar::ParagraphSeparator) { | 
					
						
							|  |  |  |             suffix.append(QLatin1String("\n\n")); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             ++firstNonSpace; | 
					
						
							|  |  |  |             if (targetFile->charAt(firstNonSpace) != QChar::ParagraphSeparator) | 
					
						
							|  |  |  |                 suffix.append(QLatin1Char('\n')); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-01-31 13:41:39 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     result += InsertionLocation(target, prefix, suffix, line, column); | 
					
						
							| 
									
										
										
										
											2010-09-27 18:01:04 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return result; | 
					
						
							| 
									
										
										
										
											2010-07-27 15:29:16 +02:00
										 |  |  | } |