void CppToolsPlugin::test_codegen_definition_empty_class() { const QByteArray srcText = "\n" "class Foo\n" // line 1 "{\n" "void foo();\n" // line 3 "};\n" "\n"; const QByteArray dstText = "\n" "int x;\n" // line 1 "\n"; Document::Ptr src = Document::create(QDir::tempPath() + QLatin1String("/file.h")); Utils::FileSaver srcSaver(src->fileName()); srcSaver.write(srcText); srcSaver.finalize(); src->setSource(srcText); src->parse(); src->check(); QCOMPARE(src->diagnosticMessages().size(), 0); QCOMPARE(src->globalSymbolCount(), 1U); Document::Ptr dst = Document::create(QDir::tempPath() + QLatin1String("/file.cpp")); Utils::FileSaver dstSaver(dst->fileName()); dstSaver.write(dstText); dstSaver.finalize(); dst->setSource(dstText); dst->parse(); dst->check(); QCOMPARE(dst->diagnosticMessages().size(), 0); QCOMPARE(dst->globalSymbolCount(), 1U); Snapshot snapshot; snapshot.insert(src); snapshot.insert(dst); Class *foo = src->globalSymbolAt(0)->asClass(); QVERIFY(foo); QCOMPARE(foo->line(), 1U); QCOMPARE(foo->column(), 7U); QCOMPARE(foo->memberCount(), 1U); Declaration *decl = foo->memberAt(0)->asDeclaration(); QVERIFY(decl); QCOMPARE(decl->line(), 3U); QCOMPARE(decl->column(), 6U); CppRefactoringChanges changes(snapshot); InsertionPointLocator find(changes); QList<InsertionLocation> locList = find.methodDefinition(decl); QVERIFY(locList.size() == 1); InsertionLocation loc = locList.first(); QCOMPARE(loc.fileName(), dst->fileName()); QCOMPARE(loc.prefix(), QLatin1String("\n\n")); QCOMPARE(loc.suffix(), QString()); QCOMPARE(loc.line(), 1U); QCOMPARE(loc.column(), 7U); }
/*! Should insert at line 3, column 1, with "public:\n" as prefix and without suffix. */ void CppToolsPlugin::test_codegen_public_in_empty_class() { const QByteArray src = "\n" "class Foo\n" // line 1 "{\n" "};\n" "\n"; Document::Ptr doc = Document::create("public_in_empty_class"); doc->setSource(src); doc->parse(); doc->check(); QCOMPARE(doc->diagnosticMessages().size(), 0); QCOMPARE(doc->globalSymbolCount(), 1U); Class *foo = doc->globalSymbolAt(0)->asClass(); QVERIFY(foo); QCOMPARE(foo->line(), 1U); QCOMPARE(foo->column(), 7U); Snapshot snapshot; snapshot.insert(doc); CppRefactoringChanges changes(snapshot); InsertionPointLocator find(changes); InsertionLocation loc = find.methodDeclarationInClass( doc->fileName(), foo, InsertionPointLocator::Public); QVERIFY(loc.isValid()); QCOMPARE(loc.prefix(), QLatin1String("public:\n")); QVERIFY(loc.suffix().isEmpty()); QCOMPARE(loc.line(), 3U); QCOMPARE(loc.column(), 1U); }
void tst_Lookup::templates_1() { const QByteArray source = "\n" "namespace std {\n" " template <typename T>\n" " struct _List_iterator {\n" " T data;\n" " };\n" "\n" " template <typename T>\n" " struct list {\n" " typedef _List_iterator<T> iterator;\n" "\n" " iterator begin();\n" " _List_iterator<T> end();\n" " };\n" "}\n" "\n" "struct Point {\n" " int x, y;\n" "};\n" "\n" "int main()\n" "{\n" " std::list<Point> l;\n" " l.begin(); // std::_List_iterator<Point> .. and not only _List_iterator<Point>\n" " l.end(); // std::_List_iterator<Point>\n" "}\n"; Document::Ptr doc = Document::create("templates_1"); doc->setSource(source); doc->parse(); doc->check(); QVERIFY(doc->diagnosticMessages().isEmpty()); }
void tst_Lookup::templates_2() { const QByteArray source = "\n" "template <typename T1>\n" "struct Node {\n" " T1 value;\n" " Node *next;\n" " Node<T1> *other_next;\n" "};\n" "\n" "template <typename T2>\n" "struct List {\n" " Node<T2> *elements;\n" "};\n" "\n" "int main()\n" "{\n" " List<int> *e;\n" " e->elements; // Node<int> *\n" " e->elements->next; // Node<int> *\n" " e->elements->other_next; // Node<int> *\n" "}\n" ; Document::Ptr doc = Document::create("templates_2"); doc->setSource(source); doc->parse(); doc->check(); QVERIFY(doc->diagnosticMessages().isEmpty()); }
void tst_Lookup::templates_5() { const QByteArray source = "\n" "struct Point {\n" " int x,y;\n" "};\n" "\n" "template <typename _Tp>\n" "struct Allocator {\n" " typedef const _Tp &const_reference;\n" "\n" " const_reference get();\n" "};\n" "\n" "int main()\n" "{\n" " Allocator<Point>::const_reference r = pt;\n" " //r.; // const Point &\n" "\n" " Allocator<Point> a;\n" " a.get(); // const Point &\n" "}\n"; Document::Ptr doc = Document::create("templates_5"); doc->setSource(source); doc->parse(); doc->check(); QVERIFY(doc->diagnosticMessages().isEmpty()); }
void tst_Lookup::class_with_baseclass() { const QByteArray source = "\n" "@implementation BaseZoo {} -(void)baseDecl; -(void)baseMethod{} @end\n" "@interface Zoo: BaseZoo {} +(id)alloc; -(id)init; @end\n" "@implementation Zoo +(id)alloc{} -(id)init{} -(void)dealloc{} @end\n"; Document::Ptr doc = Document::create("class_with_baseclass"); doc->setSource(source); doc->parse(); doc->check(); QVERIFY(doc->diagnosticMessages().isEmpty()); QCOMPARE(doc->globalSymbolCount(), 3U); Snapshot snapshot; snapshot.insert(doc); Document::Ptr emptyDoc = Document::create("<empty>"); ObjCClass *baseZoo = doc->globalSymbolAt(0)->asObjCClass(); QVERIFY(baseZoo); QVERIFY(!baseZoo->isInterface()); QCOMPARE(baseZoo->memberCount(), 2U); ObjCClass *zooIface = doc->globalSymbolAt(1)->asObjCClass(); QVERIFY(zooIface); QVERIFY(zooIface->isInterface()); QVERIFY(zooIface->baseClass()->name() == baseZoo->name()); ObjCClass *zooImpl = doc->globalSymbolAt(2)->asObjCClass(); QVERIFY(zooImpl); QVERIFY(!zooImpl->isInterface()); QCOMPARE(zooImpl->memberCount(), 3U); Declaration *baseDecl = baseZoo->memberAt(0)->asDeclaration(); QVERIFY(baseDecl); QVERIFY(baseDecl->name() && baseDecl->name()->identifier()); QCOMPARE(QLatin1String(baseDecl->name()->identifier()->chars()), QLatin1String("baseDecl")); ObjCMethod *baseMethod = baseZoo->memberAt(1)->asObjCMethod(); QVERIFY(baseMethod); QVERIFY(baseMethod->name() && baseMethod->name()->identifier()); QCOMPARE(QLatin1String(baseMethod->name()->identifier()->chars()), QLatin1String("baseMethod")); const LookupContext context(doc, snapshot); ClassOrNamespace *objClass = context.lookupType(baseZoo->name(), zooImpl->enclosingScope()); QVERIFY(objClass != 0); QVERIFY(objClass->symbols().contains(baseZoo)); QList<LookupItem> results = context.lookup(baseDecl->name(), zooImpl); QCOMPARE(results.size(), 1); QCOMPARE(results.at(0).declaration(), baseDecl); results = context.lookup(baseMethod->name(), zooImpl); QCOMPARE(results.size(), 1); QCOMPARE(results.at(0).declaration(), baseMethod); }
Document::Ptr TypeOfExpression::documentForExpression(const QString &expression) const { // create the expression's AST. Document::Ptr doc = Document::create(QLatin1String("<completion>")); const QByteArray bytes = expression.toUtf8(); doc->setSource(bytes); doc->parse(Document::ParseExpression); return doc; }
void tst_Lookup::class_with_protocol_with_protocol() { const QByteArray source = "\n" "@protocol P1 -(void)p1method; @end\n" "@protocol P2 <P1> -(void)p2method; @end\n" "@interface Zoo <P2> {} +(id)alloc; -(id)init; @end\n" "@implementation Zoo +(id)alloc{} -(id)init{} -(void)dealloc{} @end\n"; Document::Ptr doc = Document::create("class_with_protocol_with_protocol"); doc->setSource(source); doc->parse(); doc->check(); QVERIFY(doc->diagnosticMessages().isEmpty()); QCOMPARE(doc->globalSymbolCount(), 4U); Snapshot snapshot; snapshot.insert(doc); ObjCProtocol *P1 = doc->globalSymbolAt(0)->asObjCProtocol(); QVERIFY(P1); QCOMPARE(P1->memberCount(), 1U); QCOMPARE(P1->protocolCount(), 0U); Declaration *p1method = P1->memberAt(0)->asDeclaration(); QVERIFY(p1method); QCOMPARE(QLatin1String(p1method->name()->identifier()->chars()), QLatin1String("p1method")); ObjCProtocol *P2 = doc->globalSymbolAt(1)->asObjCProtocol(); QVERIFY(P2); QCOMPARE(P2->memberCount(), 1U); QCOMPARE(P2->protocolCount(), 1U); QCOMPARE(QLatin1String(P2->protocolAt(0)->name()->identifier()->chars()), QLatin1String("P1")); ObjCClass *zooImpl = doc->globalSymbolAt(3)->asObjCClass(); QVERIFY(zooImpl); const LookupContext context(doc, snapshot); { const QList<LookupItem> candidates = context.lookup(P1->name(), zooImpl->enclosingScope()); QCOMPARE(candidates.size(), 1); QVERIFY(candidates.at(0).declaration() == P1); } { const QList<LookupItem> candidates = context.lookup(P2->protocolAt(0)->name(), zooImpl->enclosingScope()); QCOMPARE(candidates.size(), 1); QVERIFY(candidates.first().declaration() == P1); } QList<LookupItem> results = context.lookup(p1method->name(), zooImpl); QCOMPARE(results.size(), 1); QCOMPARE(results.at(0).declaration(), p1method); }
/*! Should insert at line 18, column 1, with "private slots:\n" as prefix and "\n" as suffix. This is the typical Qt Designer case, with test-input like what the integration generates. */ void CppToolsPlugin::test_codegen_qtdesigner_integration() { const QByteArray src = "/**** Some long (C)opyright notice ****/\n" "#ifndef MAINWINDOW_H\n" "#define MAINWINDOW_H\n" "\n" "#include <QMainWindow>\n" "\n" "namespace Ui {\n" " class MainWindow;\n" "}\n" "\n" "class MainWindow : public QMainWindow\n" // line 10 "{\n" " Q_OBJECT\n" "\n" "public:\n" // line 14 " explicit MainWindow(QWidget *parent = 0);\n" " ~MainWindow();\n" "\n" "private:\n" // line 18 " Ui::MainWindow *ui;\n" "};\n" "\n" "#endif // MAINWINDOW_H\n"; Document::Ptr doc = Document::create("qtdesigner_integration"); doc->setSource(src); doc->parse(); doc->check(); QCOMPARE(doc->diagnosticMessages().size(), 0); QCOMPARE(doc->globalSymbolCount(), 2U); Class *foo = doc->globalSymbolAt(1)->asClass(); QVERIFY(foo); QCOMPARE(foo->line(), 10U); QCOMPARE(foo->column(), 7U); Snapshot snapshot; snapshot.insert(doc); CppRefactoringChanges changes(snapshot); InsertionPointLocator find(changes); InsertionLocation loc = find.methodDeclarationInClass( doc->fileName(), foo, InsertionPointLocator::PrivateSlot); QVERIFY(loc.isValid()); QCOMPARE(loc.prefix(), QLatin1String("private slots:\n")); QCOMPARE(loc.suffix(), QLatin1String("\n")); QCOMPARE(loc.line(), 18U); QCOMPARE(loc.column(), 1U); }
void tst_Lookup::base_class_defined_1() { Overview overview; const QByteArray source = "\n" "class base {};\n" "class derived: public base {};\n"; Document::Ptr doc = Document::create("base_class_defined_1"); doc->setSource(source); doc->parse(); doc->check(); QVERIFY(doc->diagnosticMessages().isEmpty()); QCOMPARE(doc->globalSymbolCount(), 2U); Snapshot snapshot; snapshot.insert(doc->fileName(), doc); Document::Ptr emptyDoc = Document::create("<empty>"); Class *baseClass = doc->globalSymbolAt(0)->asClass(); QVERIFY(baseClass); Class *derivedClass = doc->globalSymbolAt(1)->asClass(); QVERIFY(derivedClass); LookupContext ctx(derivedClass, emptyDoc, doc, snapshot); const QList<Symbol *> candidates = ctx.resolveClass(derivedClass->baseClassAt(0)->name()); QCOMPARE(candidates.size(), 1); QCOMPARE(candidates.at(0), baseClass); TranslationUnit *unit = doc->translationUnit(); QVERIFY(unit != 0); TranslationUnitAST *ast = unit->ast()->asTranslationUnit(); QVERIFY(ast != 0); ClassSymbols classSymbols(doc->control()); classSymbols(ast); QCOMPARE(classSymbols.size(), 2); const QMap<Class *, ClassSpecifierAST *> classToAST = invert(classSymbols.asMap()); QVERIFY(classToAST.value(baseClass) != 0); QVERIFY(classToAST.value(derivedClass) != 0); }
void tst_Lookup::base_class_defined_1() { Overview overview; const QByteArray source = "\n" "class base {};\n" "class derived: public base {};\n"; Document::Ptr doc = Document::create("base_class_defined_1"); doc->setSource(source); doc->parse(); doc->check(); QVERIFY(doc->diagnosticMessages().isEmpty()); QCOMPARE(doc->globalSymbolCount(), 2U); Snapshot snapshot; snapshot.insert(doc); Class *baseClass = doc->globalSymbolAt(0)->asClass(); QVERIFY(baseClass); Class *derivedClass = doc->globalSymbolAt(1)->asClass(); QVERIFY(derivedClass); const LookupContext ctx(doc, snapshot); ClassOrNamespace *klass = ctx.lookupType(derivedClass->baseClassAt(0)->name(), derivedClass->enclosingScope()); QVERIFY(klass != 0); QCOMPARE(klass->symbols().size(), 1); QCOMPARE(klass->symbols().first(), baseClass); TranslationUnit *unit = doc->translationUnit(); QVERIFY(unit != 0); TranslationUnitAST *ast = unit->ast()->asTranslationUnit(); QVERIFY(ast != 0); ClassSymbols classSymbols(unit); classSymbols(ast); QCOMPARE(classSymbols.size(), 2); const QMap<Class *, ClassSpecifierAST *> classToAST = invert(classSymbols.asMap()); QVERIFY(classToAST.value(baseClass) != 0); QVERIFY(classToAST.value(derivedClass) != 0); }
Document::Ptr Snapshot::documentFromSource(const QByteArray &preprocessedCode, const QString &fileName) const { Document::Ptr newDoc = Document::create(fileName); if (Document::Ptr thisDocument = document(fileName)) { newDoc->_revision = thisDocument->_revision; newDoc->_editorRevision = thisDocument->_editorRevision; newDoc->_lastModified = thisDocument->_lastModified; newDoc->_includes = thisDocument->_includes; newDoc->_definedMacros = thisDocument->_definedMacros; newDoc->_macroUses = thisDocument->_macroUses; } newDoc->setSource(preprocessedCode); return newDoc; }
void tst_Misc::diagnosticClient_warning() { const QByteArray src("\n" "using namespace ;\n" ); Document::Ptr doc = Document::create("diagnosticClient_warning"); QVERIFY(!doc.isNull()); doc->setSource(src); bool success = doc->parse(Document::ParseTranlationUnit); QVERIFY(success); QList<Document::DiagnosticMessage> diagnostics = doc->diagnosticMessages(); QVERIFY(diagnostics.size() == 1); const Document::DiagnosticMessage &msg = diagnostics.at(0); QCOMPARE(msg.level(), (int) Document::DiagnosticMessage::Warning); QCOMPARE(msg.line(), 1U); QCOMPARE(msg.column(), 17U); }
Document::Ptr basicSymbolTest(const QString &input) const { const QLatin1String filename("<lookup test>"); Document::Ptr doc = Document::create(filename); doc->setSource(input); doc->parseQml(); QList<DiagnosticMessage> msgs = doc->diagnosticMessages(); foreach (const DiagnosticMessage &msg, msgs) { if (msg.isError()) { qDebug() << "Error:" << filename << ":" << msg.loc.startLine << ":" << msg.loc.startColumn << ":" << msg.message; } else if (msg.isWarning()) { qDebug() << "Warning:" << filename << ":" << msg.loc.startLine << ":" << msg.loc.startColumn << ":" << msg.message; } else { qDebug() << "Diagnostic:" << filename << ":" << msg.loc.startLine << ":" << msg.loc.startColumn << ":" << msg.message; } } return doc; }
int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QStringList files = app.arguments(); files.removeFirst(); foreach (const QString &fileName, files) { QFile file(fileName); if (! file.open(QFile::ReadOnly)) continue; const QByteArray source = file.readAll(); file.close(); Document::Ptr doc = Document::create(fileName); doc->control()->setDiagnosticClient(0); doc->setSource(source); doc->parse(); }
void tst_Lookup::templates_4() { const QByteArray source = "\n" "template <typename T>\n" "struct Allocator {\n" " typedef T *pointer_type;\n" " typedef T &reference_type;\n" "};\n" "\n" "template <typename T>\n" "struct SharedPtr {\n" " typedef typename Allocator<T>::pointer_type pointer_type;\n" " typedef typename Allocator<T>::reference_type reference_type;\n" "\n" " pointer_type operator->();\n" " reference_type operator*();\n" "\n" " pointer_type data();\n" " reference_type get();\n" "\n" "};\n" "\n" "struct Point {\n" " int x,y;\n" "};\n" "\n" "int main()\n" "{\n" " SharedPtr<Point> l;\n" "\n" " l->x; // int\n" " (*l); // Point &\n" "}\n"; Document::Ptr doc = Document::create("templates_4"); doc->setSource(source); doc->parse(); doc->check(); QVERIFY(doc->diagnosticMessages().isEmpty()); }
void tst_Lookup::templates_3() { const QByteArray source = "\n" "struct Point {\n" " int x, y;\n" "};\n" "\n" "template <typename T = Point>\n" "struct List {\n" " const T &at(int);\n" "};\n" "\n" "int main()\n" "{\n" " List<> l;\n" " l.at(0); // const Point &\n" "}\n"; Document::Ptr doc = Document::create("templates_3"); doc->setSource(source); doc->parse(); doc->check(); QVERIFY(doc->diagnosticMessages().isEmpty()); }
Utils::ChangeSet FunctionDeclDefLink::changes(const Snapshot &snapshot, int targetOffset) { Utils::ChangeSet changes; // parse the current source declaration TypeOfExpression typeOfExpression; // ### just need to preprocess... typeOfExpression.init(sourceDocument, snapshot); QString newDeclText = linkSelection.selectedText(); for (int i = 0; i < newDeclText.size(); ++i) { if (newDeclText.at(i).toAscii() == 0) newDeclText[i] = QLatin1Char('\n'); } newDeclText.append(QLatin1String("{}")); const QString newDeclTextPreprocessed = typeOfExpression.preprocess(newDeclText); Document::Ptr newDeclDoc = Document::create(QLatin1String("<decl>")); newDeclDoc->setSource(newDeclTextPreprocessed.toUtf8()); newDeclDoc->parse(Document::ParseDeclaration); newDeclDoc->check(); // extract the function symbol if (!newDeclDoc->translationUnit()->ast()) return changes; FunctionDefinitionAST *newDef = newDeclDoc->translationUnit()->ast()->asFunctionDefinition(); if (!newDef) return changes; Function *newFunction = newDef->symbol; if (!newFunction) return changes; Overview overview; overview.setShowReturnTypes(true); overview.setShowTemplateParameters(true); overview.setShowArgumentNames(true); overview.setShowFunctionSignatures(true); // abort if the name of the newly parsed function is not the expected one DeclaratorIdAST *newDeclId = getDeclaratorId(newDef->declarator); if (!newDeclId || !newDeclId->name || !newDeclId->name->name || overview(newDeclId->name->name) != nameInitial) { return changes; } LookupContext sourceContext(sourceDocument, snapshot); LookupContext targetContext(targetFile->cppDocument(), snapshot); // sync return type { // set up for rewriting return type SubstitutionEnvironment env; env.setContext(sourceContext); env.switchScope(sourceFunction->enclosingScope()); ClassOrNamespace *targetCoN = targetContext.lookupType(targetFunction->enclosingScope()); if (!targetCoN) targetCoN = targetContext.globalNamespace(); UseMinimalNames q(targetCoN); env.enter(&q); Control *control = sourceContext.control().data(); // get return type start position and declarator info from declaration DeclaratorAST *declarator = 0; SpecifierAST *firstReplaceableSpecifier = 0; TranslationUnit *targetTranslationUnit = targetFile->cppDocument()->translationUnit(); if (SimpleDeclarationAST *simple = targetDeclaration->asSimpleDeclaration()) { declarator = simple->declarator_list->value; firstReplaceableSpecifier = findFirstReplaceableSpecifier( targetTranslationUnit, simple->decl_specifier_list); } else if (FunctionDefinitionAST *def = targetDeclaration->asFunctionDefinition()) { declarator = def->declarator; firstReplaceableSpecifier = findFirstReplaceableSpecifier( targetTranslationUnit, def->decl_specifier_list); } int returnTypeStart = 0; if (firstReplaceableSpecifier) returnTypeStart = targetFile->startOf(firstReplaceableSpecifier); else returnTypeStart = targetFile->startOf(declarator); if (!newFunction->returnType().isEqualTo(sourceFunction->returnType()) && !newFunction->returnType().isEqualTo(targetFunction->returnType())) { FullySpecifiedType type = rewriteType(newFunction->returnType(), &env, control); const QString replacement = overview(type, targetFunction->name()); changes.replace(returnTypeStart, targetFile->startOf(targetFunctionDeclarator->lparen_token), replacement); } } // sync parameters { // set up for rewriting parameter types SubstitutionEnvironment env; env.setContext(sourceContext); env.switchScope(sourceFunction); ClassOrNamespace *targetCoN = targetContext.lookupType(targetFunction); if (!targetCoN) targetCoN = targetContext.globalNamespace(); UseMinimalNames q(targetCoN); env.enter(&q); Control *control = sourceContext.control().data(); Overview overview; const unsigned sourceArgCount = declaredArgumentCount(sourceFunction); const unsigned newArgCount = declaredArgumentCount(newFunction); const unsigned targetArgCount = declaredArgumentCount(targetFunction); // check if parameter types or names have changed const unsigned existingArgs = qMin(targetArgCount, newArgCount); ParameterDeclarationClauseAST *targetParameterDecl = targetFunctionDeclarator->parameter_declaration_clause; ParameterDeclarationListAST *firstTargetParameterDeclIt = targetParameterDecl ? targetParameterDecl->parameter_declaration_list : 0; ParameterDeclarationListAST *targetParameterDeclIt = firstTargetParameterDeclIt; for (unsigned i = 0; i < existingArgs && targetParameterDeclIt; ++i, targetParameterDeclIt = targetParameterDeclIt->next) { Symbol *targetParam = targetFunction->argumentAt(i); Symbol *newParam = newFunction->argumentAt(i); // if new's name and type are the same as source's, forbid changes bool allowChangeType = true; const Name *replacementName = newParam->name(); if (i < sourceArgCount) { Symbol *sourceParam = sourceFunction->argumentAt(i); if (newParam->type().isEqualTo(sourceParam->type())) allowChangeType = false; if (namesEqual(replacementName, sourceParam->name())) replacementName = targetParam->name(); } // don't change the name if it's in a comment if (hasCommentedName(targetFile->cppDocument()->translationUnit(), targetFile->cppDocument()->source(), targetFunctionDeclarator, i)) replacementName = 0; // find the end of the parameter declaration ParameterDeclarationAST *targetParamAst = targetParameterDeclIt->value; int parameterNameEnd = 0; if (targetParamAst->declarator) parameterNameEnd = targetFile->endOf(targetParamAst->declarator); else if (targetParamAst->type_specifier_list) parameterNameEnd = targetFile->endOf(targetParamAst->type_specifier_list->lastToken() - 1); else parameterNameEnd = targetFile->startOf(targetParamAst); // change name and type? if (allowChangeType && !targetParam->type().isEqualTo(newParam->type())) { FullySpecifiedType replacementType = rewriteType(newParam->type(), &env, control); const QString replacement = overview(replacementType, replacementName); changes.replace(targetFile->startOf(targetParamAst), parameterNameEnd, replacement); } // change the name only? else if (!namesEqual(targetParam->name(), replacementName)) { DeclaratorIdAST *id = getDeclaratorId(targetParamAst->declarator); QString replacementNameStr = overview(replacementName); if (id) { changes.replace(targetFile->range(id), replacementNameStr); } else { // add name to unnamed parameter replacementNameStr.prepend(QLatin1Char(' ')); int end; if (targetParamAst->equal_token) { end = targetFile->startOf(targetParamAst->equal_token); replacementNameStr.append(QLatin1Char(' ')); } else { // one past end on purpose end = targetFile->startOf(targetParamAst->lastToken()); } changes.replace(parameterNameEnd, end, replacementNameStr); } } } // remove some parameters? if (newArgCount < targetArgCount) { targetParameterDeclIt = firstTargetParameterDeclIt; if (targetParameterDeclIt) { if (newArgCount == 0) { changes.remove(targetFile->startOf(targetParameterDeclIt->firstToken()), targetFile->endOf(targetParameterDeclIt->lastToken() - 1)); } else { // get the last valid argument for (unsigned i = 0; i < newArgCount - 1 && targetParameterDeclIt; ++i) targetParameterDeclIt = targetParameterDeclIt->next; if (targetParameterDeclIt) { const int start = targetFile->endOf(targetParameterDeclIt->value); const int end = targetFile->endOf(targetParameterDecl->lastToken() - 1); changes.remove(start, end); } } } } // add some parameters? else if (newArgCount > targetArgCount) { QString newParams; for (unsigned i = targetArgCount; i < newArgCount; ++i) { Symbol *param = newFunction->argumentAt(i); FullySpecifiedType type = rewriteType(param->type(), &env, control); if (i != 0) newParams += QLatin1String(", "); newParams += overview(type, param->name()); } targetParameterDeclIt = firstTargetParameterDeclIt; if (targetParameterDeclIt) { while (targetParameterDeclIt->next) targetParameterDeclIt = targetParameterDeclIt->next; changes.insert(targetFile->endOf(targetParameterDeclIt->value), newParams); } else { changes.insert(targetFile->endOf(targetFunctionDeclarator->lparen_token), newParams); } } } // sync cv qualification if (targetFunction->isConst() != newFunction->isConst() || targetFunction->isVolatile() != newFunction->isVolatile()) { QString cvString; if (newFunction->isConst()) cvString += QLatin1String("const"); if (newFunction->isVolatile()) { if (!cvString.isEmpty()) cvString += QLatin1Char(' '); cvString += QLatin1String("volatile"); } const int rparenEnd = targetFile->endOf(targetFunctionDeclarator->rparen_token); if (targetFunctionDeclarator->cv_qualifier_list) { const int cvEnd = targetFile->endOf(targetFunctionDeclarator->cv_qualifier_list->lastToken() - 1); // if the qualifies changed, replace if (!cvString.isEmpty()) { changes.replace(targetFile->startOf(targetFunctionDeclarator->cv_qualifier_list->firstToken()), cvEnd, cvString); } else { // remove changes.remove(rparenEnd, cvEnd); } } else { // otherwise add cvString.prepend(QLatin1Char(' ')); changes.insert(rparenEnd, cvString); } } if (targetOffset != -1) { // move all change operations to have the right start offset const int moveAmount = targetOffset - targetFile->startOf(targetDeclaration); QList<Utils::ChangeSet::EditOp> ops = changes.operationList(); for (int i = 0; i < ops.size(); ++i) { ops[i].pos1 += moveAmount; ops[i].pos2 += moveAmount; } changes = Utils::ChangeSet(ops); } return changes; }
void tst_Lookup::iface_impl_scoping() { const QByteArray source = "\n" "@interface Scooping{}-(int)method1:(int)arg;-(void)method2;@end\n" "@implementation Scooping-(int)method1:(int)arg{return arg;}@end\n"; Document::Ptr doc = Document::create("class_with_protocol_with_protocol"); doc->setSource(source); doc->parse(); doc->check(); QVERIFY(doc->diagnosticMessages().isEmpty()); QCOMPARE(doc->globalSymbolCount(), 2U); Snapshot snapshot; snapshot.insert(doc); ObjCClass *iface = doc->globalSymbolAt(0)->asObjCClass(); QVERIFY(iface); QVERIFY(iface->isInterface()); ObjCClass *impl = doc->globalSymbolAt(1)->asObjCClass(); QVERIFY(impl); QVERIFY(!impl->isInterface()); QCOMPARE(iface->memberCount(), 2U); QCOMPARE(impl->memberCount(), 1U); ObjCMethod *method1Impl = impl->memberAt(0)->asObjCMethod(); QVERIFY(method1Impl); QCOMPARE(method1Impl->identifier()->chars(), "method1"); // get the body of method1 QCOMPARE(method1Impl->memberCount(), 2U); Argument *method1Arg = method1Impl->memberAt(0)->asArgument(); QVERIFY(method1Arg); QCOMPARE(method1Arg->identifier()->chars(), "arg"); QVERIFY(method1Arg->type()->isIntegerType()); Block *method1Body = method1Impl->memberAt(1)->asBlock(); QVERIFY(method1Body); const LookupContext context(doc, snapshot); { // verify if we can resolve "arg" in the body QCOMPARE(method1Impl->argumentCount(), 1U); Argument *arg = method1Impl->argumentAt(0)->asArgument(); QVERIFY(arg); QVERIFY(arg->name()); QVERIFY(arg->name()->identifier()); QCOMPARE(arg->name()->identifier()->chars(), "arg"); QVERIFY(arg->type()->isIntegerType()); const QList<LookupItem> candidates = context.lookup(arg->name(), method1Body->enclosingScope()); QCOMPARE(candidates.size(), 1); QVERIFY(candidates.at(0).declaration()->type()->asIntegerType()); } Declaration *method2 = iface->memberAt(1)->asDeclaration(); QVERIFY(method2); QCOMPARE(method2->identifier()->chars(), "method2"); { // verify if we can resolve "method2" in the body const QList<LookupItem> candidates = context.lookup(method2->name(), method1Body->enclosingScope()); QCOMPARE(candidates.size(), 1); QCOMPARE(candidates.at(0).declaration(), method2); } }
void tst_Lookup::simple_class_1() { const QByteArray source = "\n" "@interface Zoo {} +(id)alloc; -(id)init; @end\n" "@implementation Zoo +(id)alloc{} -(id)init{} -(void)dealloc{} @end\n"; Document::Ptr doc = Document::create("simple_class_1"); doc->setSource(source); doc->parse(); doc->check(); QVERIFY(doc->diagnosticMessages().isEmpty()); QCOMPARE(doc->globalSymbolCount(), 2U); Snapshot snapshot; snapshot.insert(doc); ObjCClass *iface = doc->globalSymbolAt(0)->asObjCClass(); QVERIFY(iface); QVERIFY(iface->isInterface()); QCOMPARE(iface->memberCount(), 2U); ObjCClass *impl = doc->globalSymbolAt(1)->asObjCClass(); QVERIFY(impl); QVERIFY(!impl->isInterface()); QCOMPARE(impl->memberCount(), 3U); Declaration *allocMethodIface = iface->memberAt(0)->asDeclaration(); QVERIFY(allocMethodIface); QVERIFY(allocMethodIface->name() && allocMethodIface->name()->identifier()); QCOMPARE(QLatin1String(allocMethodIface->name()->identifier()->chars()), QLatin1String("alloc")); ObjCMethod *allocMethodImpl = impl->memberAt(0)->asObjCMethod(); QVERIFY(allocMethodImpl); QVERIFY(allocMethodImpl->name() && allocMethodImpl->name()->identifier()); QCOMPARE(QLatin1String(allocMethodImpl->name()->identifier()->chars()), QLatin1String("alloc")); ObjCMethod *deallocMethod = impl->memberAt(2)->asObjCMethod(); QVERIFY(deallocMethod); QVERIFY(deallocMethod->name() && deallocMethod->name()->identifier()); QCOMPARE(QLatin1String(deallocMethod->name()->identifier()->chars()), QLatin1String("dealloc")); const LookupContext context(doc, snapshot); // check class resolving: ClassOrNamespace *klass = context.lookupType(impl->name(), impl->enclosingScope()); QVERIFY(klass != 0); QCOMPARE(klass->symbols().size(), 2); QVERIFY(klass->symbols().contains(iface)); QVERIFY(klass->symbols().contains(impl)); // check method resolving: QList<LookupItem> results = context.lookup(allocMethodImpl->name(), impl); QCOMPARE(results.size(), 2); QCOMPARE(results.at(0).declaration(), allocMethodIface); QCOMPARE(results.at(1).declaration(), allocMethodImpl); results = context.lookup(deallocMethod->name(), impl); QCOMPARE(results.size(), 1); QCOMPARE(results.at(0).declaration(), deallocMethod); }