diff --git a/src/libs/utils/changeset.cpp b/src/libs/utils/changeset.cpp index 455ce62855e..2f43e5779d4 100644 --- a/src/libs/utils/changeset.cpp +++ b/src/libs/utils/changeset.cpp @@ -158,6 +158,8 @@ bool ChangeSet::move_helper(int pos, int length, int to) bool ChangeSet::insert(int pos, const QString &text) { + Q_ASSERT(pos >= 0); + if (hasOverlap(pos, 0)) m_error = true; diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index 5fc351a3b68..604246173b4 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -46,4 +46,9 @@ SOURCES += cppplugin.cpp \ RESOURCES += cppeditor.qrc OTHER_FILES += CppEditor.mimetypes.xml +equals(TEST, 1) { + SOURCES += \ + cppquickfix_test.cpp + DEFINES += SRCDIR=\\\"$$PWD\\\" +} diff --git a/src/plugins/cppeditor/cppinsertdecldef.cpp b/src/plugins/cppeditor/cppinsertdecldef.cpp index f1616454d7e..44b95e09612 100644 --- a/src/plugins/cppeditor/cppinsertdecldef.cpp +++ b/src/plugins/cppeditor/cppinsertdecldef.cpp @@ -57,6 +57,7 @@ using namespace CppEditor; using namespace CppEditor::Internal; using namespace CppTools; using namespace TextEditor; +using namespace Utils; namespace { @@ -318,6 +319,185 @@ void DefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOperation namespace { +class GetterSetterOperation : public CppQuickFixOperation +{ +public: + GetterSetterOperation(const QSharedPointer &interface) + : CppQuickFixOperation(interface) + { + setDescription(TextEditor::QuickFixFactory::tr("Create Getter and Setter Member Functions")); + + m_variableName = 0; + m_declaratorId = 0; + m_declarator = 0; + m_variableDecl = 0; + m_classSpecifier = 0; + m_classDecl = 0; + + const QList &path = interface->path(); + // We expect something like + // [0] TranslationUnitAST + // [1] NamespaceAST + // [2] LinkageBodyAST + // [3] SimpleDeclarationAST + // [4] ClassSpecifierAST + // [5] SimpleDeclarationAST + // [6] DeclaratorAST + // [7] DeclaratorIdAST + // [8] SimpleNameAST + + const int n = path.size(); + if (n < 6) + return; + + m_variableName = path.at(n - 1)->asSimpleName(); + m_declaratorId = path.at(n - 2)->asDeclaratorId(); + m_declarator = path.at(n - 3)->asDeclarator(); + m_variableDecl = path.at(n - 4)->asSimpleDeclaration(); + m_classSpecifier = path.at(n - 5)->asClassSpecifier(); + m_classDecl = path.at(n - 6)->asSimpleDeclaration(); + } + + bool isValid() const + { + return m_variableName + && m_declaratorId + && m_declarator + && m_variableDecl + && m_classSpecifier + && m_classDecl; + } + + void perform() + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.file(fileName()); + + const Name *variableName = m_variableName->name; + QTC_ASSERT(variableName, return); + const Identifier *variableId = variableName->identifier(); + QTC_ASSERT(variableId, return); + QString variableString = QString::fromLatin1(variableId->chars(), variableId->size()); + + const List *symbols = m_variableDecl->symbols; + QTC_ASSERT(symbols, return); + Symbol *symbol = symbols->value; + QTC_ASSERT(symbol, return); + FullySpecifiedType fullySpecifiedType = symbol->type(); + Type *type = fullySpecifiedType.type(); + QTC_ASSERT(type, return); + Overview oo; + oo.showFunctionSignatures = true; + oo.showReturnTypes = true; + oo.showArgumentNames = true; + QString typeString = oo.prettyType(fullySpecifiedType); + + const NameAST *classNameAST = m_classSpecifier->name; + QTC_ASSERT(classNameAST, return); + const Name *className = classNameAST->name; + QTC_ASSERT(className, return); + const Identifier *classId = className->identifier(); + QTC_ASSERT(classId, return); + QString classString = QString::fromLatin1(classId->chars(), classId->size()); + + bool wasHeader = true; + QString declFileName = currentFile->fileName(); + QString implFileName = CppTools::correspondingHeaderOrSource(declFileName, &wasHeader); + const bool sameFile = !wasHeader || !QFile::exists(implFileName); + if (sameFile) + implFileName = declFileName; + + InsertionPointLocator locator(refactoring); + InsertionLocation declLocation = locator.methodDeclarationInClass + (declFileName, m_classSpecifier->symbol->asClass(), InsertionPointLocator::Public); + + QString baseName = variableString; + if (baseName.startsWith(QLatin1String("m_"))) + baseName.remove(0, 2); + else if (baseName.startsWith(QLatin1Char('_'))) + baseName.remove(0, 1); + else if (baseName.endsWith(QLatin1Char('_'))) + baseName.chop(1); + + QString getterName = QString::fromLatin1("%1").arg(baseName); + QString setterName = QString::fromLatin1("set%1%2") + .arg(baseName.left(1).toUpper()).arg(baseName.mid(1)); + + const bool passByValue = type->isIntegerType() || type->isFloatType() + || type->isPointerType() || type->isEnumType(); + const char *param = passByValue ? "%1" : "const %1 &"; + QString paramString = QString::fromLatin1(param).arg(typeString); + + QString declaration = declLocation.prefix(); + declaration += QString::fromLatin1( + "%3 %1() const;\n" "void %2(%4 value);\n") + .arg(getterName).arg(setterName) + .arg(typeString).arg(paramString); + declaration += declLocation.suffix(); + + QString implementation = QString::fromLatin1( + "\n%5 %3::%1() const\n" + "{\n" + "return %4;\n" + "}\n" + "\nvoid %3::%2(%6 value)\n" + "{\n" + "%4 = value;\n" + "}\n") + .arg(getterName).arg(setterName) + .arg(classString).arg(variableString) + .arg(typeString).arg(paramString); + + ChangeSet currChanges; + + int declInsertPos = currentFile->position(qMax(1u, declLocation.line()), + declLocation.column()); + currChanges.insert(declInsertPos, declaration); + + if (sameFile) { + const int pos = currentFile->endOf(m_classDecl) + 1; + unsigned line, column; + currentFile->lineAndColumn(pos, &line, &column); + const int insertPos = currentFile->position(line + 1, 1) - 1; + currChanges.insert(insertPos < 0 ? pos : insertPos, implementation); + } else { + CppRefactoringChanges implRefactoring(snapshot()); + CppRefactoringFilePtr implFile = implRefactoring.file(implFileName); + ChangeSet implChanges; + const int implInsertPos = QFileInfo(implFileName).size(); + implChanges.insert(implInsertPos, implementation); + implFile->setChangeSet(implChanges); + implFile->appendIndentRange( + ChangeSet::Range(implInsertPos, implInsertPos + implementation.size())); + implFile->apply(); + } + currentFile->setChangeSet(currChanges); + currentFile->appendIndentRange( + ChangeSet::Range(declInsertPos, declInsertPos + declaration.size())); + currentFile->apply(); + } + + SimpleNameAST *m_variableName; + DeclaratorIdAST *m_declaratorId; + DeclaratorAST *m_declarator; + SimpleDeclarationAST *m_variableDecl; + ClassSpecifierAST *m_classSpecifier; + SimpleDeclarationAST *m_classDecl; +}; + +} // namespace + +void GetterSetter::match(const CppQuickFixInterface &interface, QuickFixOperations &result) +{ + GetterSetterOperation *op = new GetterSetterOperation(interface); + if (op->isValid()) + result.append(CppQuickFixOperation::Ptr(op)); + else + delete op; +} + +namespace { + class ExtractFunctionOperation : public CppQuickFixOperation { public: diff --git a/src/plugins/cppeditor/cppinsertdecldef.h b/src/plugins/cppeditor/cppinsertdecldef.h index 950e20d294e..ab308ef6495 100644 --- a/src/plugins/cppeditor/cppinsertdecldef.h +++ b/src/plugins/cppeditor/cppinsertdecldef.h @@ -49,6 +49,13 @@ public: class ExtractFunction : public CppQuickFixFactory { +public: + void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result); +}; + +class GetterSetter : public CppQuickFixFactory +{ +public: void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result); }; diff --git a/src/plugins/cppeditor/cppplugin.h b/src/plugins/cppeditor/cppplugin.h index 215db30c5f7..bec0fdbb7a7 100644 --- a/src/plugins/cppeditor/cppplugin.h +++ b/src/plugins/cppeditor/cppplugin.h @@ -87,6 +87,11 @@ private slots: void currentEditorChanged(Core::IEditor *editor); void openTypeHierarchy(); +#ifdef WITH_TESTS +private slots: // quickfix tests + void test_quickfix_GetterSetter(); +#endif // WITH_TESTS + private: Core::IEditor *createEditor(QWidget *parent); void writeSettings(); diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp new file mode 100644 index 00000000000..25218061ce6 --- /dev/null +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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. +** +** GNU Lesser General Public License Usage +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/*! + Tests for quick-fixes. + */ +using namespace CPlusPlus; +using namespace CppEditor; +using namespace CppEditor::Internal; +using namespace CppTools; +using namespace TextEditor; +using namespace Core; + +namespace { +/** + * Encapsulates the whole process of setting up an editor, getting the + * quick-fix, applying it, and checking the result. + */ +struct TestCase +{ + QByteArray originalText; + int pos; + CPPEditor *editor; + CPPEditorWidget *editorWidget; + + TestCase(const QByteArray &input); + ~TestCase(); + + QuickFixOperation::Ptr getFix(CppQuickFixFactory *factory); + + void run(CppQuickFixFactory *factory, const QByteArray &expected, int undoCount = 1); + +private: + TestCase(const TestCase &); + TestCase &operator=(const TestCase &); +}; + +/// apply the factory on the source, and get back the first result. +QuickFixOperation::Ptr TestCase::getFix(CppQuickFixFactory *factory) +{ + CppQuickFixInterface qfi(new CppQuickFixAssistInterface(editorWidget, ExplicitlyInvoked)); + TextEditor::QuickFixOperations results; + factory->match(qfi, results); + Q_ASSERT(!results.isEmpty()); + return results.first(); +} + +/// The '@' in the input is the position from where the quick-fix discovery is triggered. +TestCase::TestCase(const QByteArray &input) + : originalText(input) +{ + pos = originalText.indexOf('@'); + QVERIFY(pos != -1); + originalText.remove(pos, 1); + QString fileName(QDir::tempPath() + QLatin1String("/file.cpp")); + Utils::FileSaver srcSaver(fileName); + srcSaver.write(originalText); + srcSaver.finalize(); + CPlusPlus::CppModelManagerInterface::instance()->updateSourceFiles(QStringList()<snapshot(); + if (s.contains(fileName)) + break; + QCoreApplication::processEvents(); + } + + editor = dynamic_cast(EditorManager::openEditor(fileName)); + QVERIFY(editor); + editor->setCursorPosition(pos); + editorWidget = dynamic_cast(editor->editorWidget()); + QVERIFY(editorWidget); + editorWidget->semanticRehighlight(true); + + // wait for the semantic info from the future: + while (editorWidget->semanticInfo().doc.isNull()) + QCoreApplication::processEvents(); +} + +TestCase::~TestCase() +{ + EditorManager::instance()->closeEditors(QList() << editor, + false); + QCoreApplication::processEvents(); // process any pending events + + // Remove the test file from the code-model: + CppModelManagerInterface *mmi = CPlusPlus::CppModelManagerInterface::instance(); + mmi->GC(); + QCOMPARE(mmi->snapshot().size(), 0); +} + +/// Leading whitespace is not removed, so we can check if the indetation ranges +/// have been set correctly by the quick-fix. +QByteArray &removeTrailingWhitespace(QByteArray &input) +{ + QList lines = input.split('\n'); + input.resize(0); + foreach (QByteArray line, lines) { + while (line.length() > 0) { + char lastChar = line[line.length() - 1]; + if (lastChar == ' ' || lastChar == '\t') + line = line.left(line.length() - 1); + else + break; + } + input.append(line); + input.append('\n'); + } + return input; +} + +void TestCase::run(CppQuickFixFactory *factory, const QByteArray &expected, int undoCount) +{ + QuickFixOperation::Ptr fix = getFix(factory); + fix->perform(); + QByteArray result = editorWidget->document()->toPlainText().toUtf8(); + removeTrailingWhitespace(result); + + QCOMPARE(result, expected); + + for (int i = 0; i < undoCount; ++i) + editorWidget->undo(); + + result = editorWidget->document()->toPlainText().toUtf8(); + QCOMPARE(result, originalText); +} +} // anonymous namespace + +void CppPlugin::test_quickfix_GetterSetter() +{ + TestCase data("\n" + "class Something\n" + "{\n" + " int @it;\n" + "};\n" + ); + QByteArray expected = "\n" + "class Something\n" + "{\n" + " int it;\n" + "\n" + "public:\n" + " int it() const;\n" + " void setIt(int value);\n" + "};\n" + "\n" + "int Something::it() const\n" + "{\n" + " return it;\n" + "}\n" + "\n" + "void Something::setIt(int value)\n" + "{\n" + " it = value;\n" + "}\n" + "\n" + ; + + GetterSetter factory; + data.run(&factory, expected); +} diff --git a/src/plugins/cppeditor/cppquickfixassistant.cpp b/src/plugins/cppeditor/cppquickfixassistant.cpp index 62aaca234cb..7005aa911b7 100644 --- a/src/plugins/cppeditor/cppquickfixassistant.cpp +++ b/src/plugins/cppeditor/cppquickfixassistant.cpp @@ -104,6 +104,7 @@ CppQuickFixAssistInterface::CppQuickFixAssistInterface(CPPEditorWidget *editor, , m_currentFile(CppRefactoringChanges::file(editor, m_semanticInfo.doc)) , m_context(m_semanticInfo.doc, m_snapshot) { + Q_ASSERT(!m_semanticInfo.doc.isNull()); CPlusPlus::ASTPath astPath(m_semanticInfo.doc); m_path = astPath(editor->textCursor()); } diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 5a8ddf1dfac..db9f3a675ae 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -2174,6 +2174,7 @@ void registerQuickFixes(ExtensionSystem::IPlugin *plugIn) plugIn->addAutoReleasedObject(new ApplyDeclDefLinkChanges); plugIn->addAutoReleasedObject(new IncludeAdder); plugIn->addAutoReleasedObject(new ExtractFunction); + plugIn->addAutoReleasedObject(new GetterSetter); plugIn->addAutoReleasedObject(new RearrangeParamDeclList); plugIn->addAutoReleasedObject(new PointerDeclarationFormatterOp); } diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp index d90d73d3aca..6f0caaac81c 100644 --- a/src/plugins/cpptools/cppcompletion_test.cpp +++ b/src/plugins/cpptools/cppcompletion_test.cpp @@ -63,7 +63,7 @@ using namespace CppTools::Internal; using namespace TextEditor; using namespace Core; -struct TestData +struct TestCase { QByteArray srcText; int pos; @@ -72,7 +72,7 @@ struct TestData QTextDocument *doc; }; -static QStringList getCompletions(TestData &data) +static QStringList getCompletions(TestCase &data) { QStringList completions; @@ -96,7 +96,7 @@ static QStringList getCompletions(TestData &data) return completions; } -static void setup(TestData *data) +static void setup(TestCase *data) { data->pos = data->srcText.indexOf('@'); QVERIFY(data->pos != -1); @@ -120,7 +120,7 @@ static void setup(TestData *data) void CppToolsPlugin::test_completion_forward_declarations_present() { - TestData data; + TestCase data; data.srcText = "\n" "class Foo\n" "{\n" @@ -154,7 +154,7 @@ void CppToolsPlugin::test_completion_forward_declarations_present() void CppToolsPlugin::test_completion_inside_parentheses_c_style_conversion() { - TestData data; + TestCase data; data.srcText = "\n" "class Base\n" "{\n" @@ -195,7 +195,7 @@ void CppToolsPlugin::test_completion_inside_parentheses_c_style_conversion() void CppToolsPlugin::test_completion_inside_parentheses_cast_operator_conversion() { - TestData data; + TestCase data; data.srcText = "\n" "class Base\n" "{\n" @@ -235,7 +235,7 @@ void CppToolsPlugin::test_completion_inside_parentheses_cast_operator_conversion void CppToolsPlugin::test_completion_basic_1() { - TestData data; + TestCase data; data.srcText = "\n" "class Foo\n" "{\n" @@ -275,7 +275,7 @@ void CppToolsPlugin::test_completion_basic_1() void CppToolsPlugin::test_completion_template_1() { - TestData data; + TestCase data; data.srcText = "\n" "template \n" "class Foo\n" @@ -311,7 +311,7 @@ void CppToolsPlugin::test_completion_template_1() void CppToolsPlugin::test_completion_template_2() { - TestData data; + TestCase data; data.srcText = "\n" "template \n" "struct List\n" @@ -346,7 +346,7 @@ void CppToolsPlugin::test_completion_template_2() void CppToolsPlugin::test_completion_template_3() { - TestData data; + TestCase data; data.srcText = "\n" "template \n" "struct List\n" @@ -381,7 +381,7 @@ void CppToolsPlugin::test_completion_template_3() void CppToolsPlugin::test_completion_template_4() { - TestData data; + TestCase data; data.srcText = "\n" "template \n" "struct List\n" @@ -417,7 +417,7 @@ void CppToolsPlugin::test_completion_template_4() void CppToolsPlugin::test_completion_template_5() { - TestData data; + TestCase data; data.srcText = "\n" "template \n" "struct List\n" @@ -453,7 +453,7 @@ void CppToolsPlugin::test_completion_template_5() void CppToolsPlugin::test_completion_template_6() { - TestData data; + TestCase data; data.srcText = "\n" "class Item\n" "{\n" @@ -493,7 +493,7 @@ void CppToolsPlugin::test_completion_template_6() void CppToolsPlugin::test_completion_template_7() { - TestData data; + TestCase data; data.srcText = "\n" "struct Test\n" "{\n" @@ -535,7 +535,7 @@ void CppToolsPlugin::test_completion_template_7() void CppToolsPlugin::test_completion_type_of_pointer_is_typedef() { - TestData data; + TestCase data; data.srcText = "\n" "typedef struct Foo\n" "{\n" @@ -566,7 +566,7 @@ void CppToolsPlugin::test_completion() QFETCH(QByteArray, code); QFETCH(QStringList, expectedCompletions); - TestData data; + TestCase data; data.srcText = code; setup(&data); @@ -1207,7 +1207,7 @@ void CppToolsPlugin::test_completion_enclosing_template_class_data() void CppToolsPlugin::test_completion_instantiate_nested_class_when_enclosing_is_template() { - TestData data; + TestCase data; data.srcText = "\n" "struct Foo \n" "{\n" @@ -1247,7 +1247,7 @@ void CppToolsPlugin::test_completion_instantiate_nested_class_when_enclosing_is_ void CppToolsPlugin::test_completion_instantiate_nested_of_nested_class_when_enclosing_is_template() { - TestData data; + TestCase data; data.srcText = "\n" "struct Foo \n" "{\n" diff --git a/src/plugins/cpptools/insertionpointlocator.cpp b/src/plugins/cpptools/insertionpointlocator.cpp index d30101c085c..a5c9ee6e752 100644 --- a/src/plugins/cpptools/insertionpointlocator.cpp +++ b/src/plugins/cpptools/insertionpointlocator.cpp @@ -95,18 +95,27 @@ struct AccessRange unsigned start; unsigned end; InsertionPointLocator::AccessSpec xsSpec; + unsigned colonToken; AccessRange() : start(0) , end(0) , xsSpec(InsertionPointLocator::Invalid) + , colonToken(0) {} - AccessRange(unsigned start, unsigned end, InsertionPointLocator::AccessSpec xsSpec) + AccessRange(unsigned start, unsigned end, InsertionPointLocator::AccessSpec xsSpec, unsigned colonToken) : start(start) , end(end) , xsSpec(xsSpec) + , colonToken(colonToken) {} + + bool isEmpty() const + { + unsigned contentStart = 1 + (colonToken ? colonToken : start); + return contentStart == end; + } }; class FindInClass: public ASTVisitor @@ -146,16 +155,19 @@ protected: ast->rbrace_token); unsigned beforeToken = 0; + bool needsLeadingEmptyLine = false; bool needsPrefix = false; bool needsSuffix = false; - findMatch(ranges, _xsSpec, beforeToken, needsPrefix, needsSuffix); + findMatch(ranges, _xsSpec, beforeToken, needsLeadingEmptyLine, needsPrefix, needsSuffix); unsigned line = 0, column = 0; getTokenStartPosition(beforeToken, &line, &column); QString prefix; + if (needsLeadingEmptyLine) + prefix += QLatin1String("\n"); if (needsPrefix) - prefix = generate(_xsSpec); + prefix += generate(_xsSpec); QString suffix; if (needsSuffix) @@ -169,12 +181,15 @@ protected: static void findMatch(const QList &ranges, InsertionPointLocator::AccessSpec xsSpec, unsigned &beforeToken, + bool &needsLeadingEmptyLine, bool &needsPrefix, bool &needsSuffix) { QTC_ASSERT(!ranges.isEmpty(), return); const int lastIndex = ranges.size() - 1; + needsLeadingEmptyLine = false; + // try an exact match, and ignore the first (default) access spec: for (int i = lastIndex; i > 0; --i) { const AccessRange &range = ranges.at(i); @@ -200,6 +215,7 @@ protected: // otherwise: beforeToken = ranges.first().end; + needsLeadingEmptyLine = !ranges.first().isEmpty(); needsPrefix = true; needsSuffix = (ranges.size() != 1); } @@ -210,14 +226,14 @@ protected: int lastRangeEnd) const { QList ranges; - ranges.append(AccessRange(firstRangeStart, lastRangeEnd, initialXs)); + ranges.append(AccessRange(firstRangeStart, lastRangeEnd, initialXs, 0)); 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; + InsertionPointLocator::AccessSpec newXsSpec = initialXs; bool isSlot = xsDecl->slots_token && tokenKind(xsDecl->slots_token) == T_Q_SLOTS; @@ -242,7 +258,8 @@ protected: break; case T_Q_SLOTS: { - newXsSpec = ranges.last().xsSpec | InsertionPointLocator::SlotBit; + newXsSpec = (InsertionPointLocator::AccessSpec) + (ranges.last().xsSpec | InsertionPointLocator::SlotBit); break; } @@ -250,9 +267,10 @@ protected: break; } - if (newXsSpec != ranges.last().xsSpec) { + if (newXsSpec != ranges.last().xsSpec || ranges.size() == 1) { ranges.last().end = token; - ranges.append(AccessRange(token, lastRangeEnd, (InsertionPointLocator::AccessSpec) newXsSpec)); + AccessRange r(token, lastRangeEnd, newXsSpec, xsDecl->colon_token); + ranges.append(r); } } }