bool CheckExpression::visit(QtMethodAST *ast) { const Name *name = 0; Scope dummy; FullySpecifiedType methTy = semantic()->check(ast->declarator, FullySpecifiedType(), &dummy, &name); Function *fty = methTy->asFunctionType(); if (! fty) translationUnit()->warning(ast->firstToken(), "expected a function declarator"); else { for (unsigned i = 0; i < fty->argumentCount(); ++i) { Symbol *arg = fty->argumentAt(i); if (arg->name()) translationUnit()->warning(arg->sourceLocation(), "argument should be anonymous"); } } return false; }
bool CheckDeclaration::visit(FunctionDefinitionAST *ast) { FullySpecifiedType ty = semantic()->check(ast->decl_specifier_seq, _scope); FullySpecifiedType qualTy = ty.qualifiedType(); Name *name = 0; FullySpecifiedType funTy = semantic()->check(ast->declarator, qualTy, _scope, &name); if (! (funTy && funTy->isFunctionType())) { translationUnit()->error(ast->firstToken(), "expected a function prototype"); return false; } Function *fun = funTy->asFunctionType(); fun->setVirtual(ty.isVirtual()); fun->setStartOffset(tokenAt(ast->firstToken()).offset); fun->setEndOffset(tokenAt(ast->lastToken()).offset); if (ast->declarator) fun->setSourceLocation(ast->declarator->firstToken()); fun->setName(name); fun->setTemplateParameters(_templateParameters); fun->setVisibility(semantic()->currentVisibility()); fun->setMethodKey(semantic()->currentMethodKey()); const bool isQ_SLOT = ast->qt_invokable_token && tokenKind(ast->qt_invokable_token) == T_Q_SLOT; const bool isQ_SIGNAL = ast->qt_invokable_token && tokenKind(ast->qt_invokable_token) == T_Q_SIGNAL; if (isQ_SIGNAL) fun->setMethodKey(Function::SignalMethod); else if (isQ_SLOT) fun->setMethodKey(Function::SlotMethod); checkFunctionArguments(fun); ast->symbol = fun; _scope->enterSymbol(fun); if (! semantic()->skipFunctionBodies()) { if (ast->ctor_initializer) { bool looksLikeCtor = false; if (ty.isValid() || ! fun->identity()) looksLikeCtor = false; else if (fun->identity()->isNameId() || fun->identity()->isTemplateNameId()) looksLikeCtor = true; if (! looksLikeCtor) { translationUnit()->error(ast->ctor_initializer->firstToken(), "only constructors take base initializers"); } accept(ast->ctor_initializer); } const int previousVisibility = semantic()->switchVisibility(Symbol::Public); const int previousMethodKey = semantic()->switchMethodKey(Function::NormalMethod); semantic()->check(ast->function_body, fun->members()); semantic()->switchMethodKey(previousMethodKey); semantic()->switchVisibility(previousVisibility); } return false; }
bool CheckDeclaration::visit(SimpleDeclarationAST *ast) { FullySpecifiedType ty = semantic()->check(ast->decl_specifier_seq, _scope); FullySpecifiedType qualTy = ty.qualifiedType(); if (_templateParameters && ty) { if (Class *klass = ty->asClassType()) { klass->setTemplateParameters(_templateParameters); } } if (! ast->declarators && ast->decl_specifier_seq && ! ast->decl_specifier_seq->next) { if (ElaboratedTypeSpecifierAST *elab_type_spec = ast->decl_specifier_seq->asElaboratedTypeSpecifier()) { unsigned sourceLocation = elab_type_spec->firstToken(); if (elab_type_spec->name) sourceLocation = elab_type_spec->name->firstToken(); Name *name = semantic()->check(elab_type_spec->name, _scope); ForwardClassDeclaration *symbol = control()->newForwardClassDeclaration(sourceLocation, name); if (_templateParameters) { symbol->setTemplateParameters(_templateParameters); _templateParameters = 0; } _scope->enterSymbol(symbol); return false; } } const bool isQ_SLOT = ast->qt_invokable_token && tokenKind(ast->qt_invokable_token) == T_Q_SLOT; const bool isQ_SIGNAL = ast->qt_invokable_token && tokenKind(ast->qt_invokable_token) == T_Q_SIGNAL; List<Declaration *> **decl_it = &ast->symbols; for (DeclaratorListAST *it = ast->declarators; it; it = it->next) { Name *name = 0; FullySpecifiedType declTy = semantic()->check(it->declarator, qualTy, _scope, &name); unsigned location = locationOfDeclaratorId(it->declarator); if (! location) { if (it->declarator) location = it->declarator->firstToken(); else location = ast->firstToken(); } Function *fun = 0; if (declTy && 0 != (fun = declTy->asFunctionType())) { fun->setSourceLocation(location); fun->setScope(_scope); fun->setName(name); fun->setMethodKey(semantic()->currentMethodKey()); fun->setVirtual(ty.isVirtual()); if (isQ_SIGNAL) fun->setMethodKey(Function::SignalMethod); else if (isQ_SLOT) fun->setMethodKey(Function::SlotMethod); fun->setVisibility(semantic()->currentVisibility()); } else if (semantic()->currentMethodKey() != Function::NormalMethod) { translationUnit()->warning(ast->firstToken(), "expected a function declaration"); } Declaration *symbol = control()->newDeclaration(location, name); symbol->setStartOffset(tokenAt(ast->firstToken()).offset); symbol->setEndOffset(tokenAt(ast->lastToken()).offset); symbol->setType(control()->integerType(IntegerType::Int)); symbol->setType(declTy); if (_templateParameters && it == ast->declarators && ty && ! ty->isClassType()) symbol->setTemplateParameters(_templateParameters); symbol->setVisibility(semantic()->currentVisibility()); if (ty.isFriend()) symbol->setStorage(Symbol::Friend); else if (ty.isRegister()) symbol->setStorage(Symbol::Register); else if (ty.isStatic()) symbol->setStorage(Symbol::Static); else if (ty.isExtern()) symbol->setStorage(Symbol::Extern); else if (ty.isMutable()) symbol->setStorage(Symbol::Mutable); else if (ty.isTypedef()) symbol->setStorage(Symbol::Typedef); if (it->declarator && it->declarator->initializer) { FullySpecifiedType initTy = semantic()->check(it->declarator->initializer, _scope); } *decl_it = new (translationUnit()->memoryPool()) List<Declaration *>(); (*decl_it)->value = symbol; decl_it = &(*decl_it)->next; _scope->enterSymbol(symbol); } return false; }
/*! Performs some further checks and rewrites the type and name of \a symbol into the substitution range in the file specified by \a tokenRange. */ void PointerDeclarationFormatter::checkAndRewrite(DeclaratorAST *declarator, Symbol *symbol, TokenRange tokenRange, unsigned charactersToRemove) { CHECK_R(tokenRange.end > 0, "TokenRange invalid1"); CHECK_R(tokenRange.start < tokenRange.end, "TokenRange invalid2"); CHECK_R(symbol, "No symbol"); // Check for expanded tokens for (unsigned token = tokenRange.start; token <= tokenRange.end; ++token) CHECK_R(!tokenAt(token).expanded(), "Token is expanded"); Range range(m_cppRefactoringFile->startOf(tokenRange.start), m_cppRefactoringFile->endOf(tokenRange.end)); CHECK_R(range.start >= 0 && range.end > 0, "ChangeRange invalid1"); CHECK_R(range.start < range.end, "ChangeRange invalid2"); // Check range with respect to cursor position / selection if (m_cursorHandling == RespectCursor) { const QTextCursor cursor = m_cppRefactoringFile->cursor(); if (cursor.hasSelection()) { CHECK_R(cursor.selectionStart() <= range.start, "Change not in selection range"); CHECK_R(range.end <= cursor.selectionEnd(), "Change not in selection range"); } else { CHECK_R(range.start <= cursor.selectionStart(), "Cursor before activation range"); CHECK_R(cursor.selectionEnd() <= range.end, "Cursor after activation range"); } } FullySpecifiedType type = symbol->type(); if (Function *function = type->asFunctionType()) type = function->returnType(); // Check if pointers or references are involved const QString originalDeclaration = m_cppRefactoringFile->textOf(range); CHECK_R(originalDeclaration.contains(QLatin1Char('&')) || originalDeclaration.contains(QLatin1Char('*')), "No pointer or references"); // Does the rewritten declaration (part) differs from the original source (part)? QString rewrittenDeclaration; const Name *name = symbol->name(); if (name) { if (name->isOperatorNameId() || (name->isQualifiedNameId() && name->asQualifiedNameId()->name()->isOperatorNameId())) { const QString operatorText = m_cppRefactoringFile->textOf(declarator->core_declarator); m_overview.includeWhiteSpaceInOperatorName = operatorText.contains(QLatin1Char(' ')); } } rewrittenDeclaration = m_overview.prettyType(type, name); rewrittenDeclaration.remove(0, charactersToRemove); CHECK_R(originalDeclaration != rewrittenDeclaration, "Rewritten is same as original"); CHECK_R(rewrittenDeclaration.contains(QLatin1Char('&')) || rewrittenDeclaration.contains(QLatin1Char('*')), "No pointer or references in rewritten declaration"); if (DEBUG_OUTPUT) { qDebug("==> Rewritten: \"%s\" --> \"%s\"", originalDeclaration.toLatin1().constData(), rewrittenDeclaration.toLatin1().constData()); } // Creating the replacement in the changeset may fail due to operations // in the changeset that overlap with the current range. // // Consider this case: // // void (*foo)(char * s) = 0; // // First visit(SimpleDeclarationAST *ast) will be called. It creates a // replacement that also includes the parameter. // Next visit(ParameterDeclarationAST *ast) is called with the // original source. It tries to create an replacement operation // at this position and fails due to overlapping ranges (the // simple declaration range includes parameter declaration range). ChangeSet change(m_changeSet); if (change.replace(range, rewrittenDeclaration)) m_changeSet = change; else if (DEBUG_OUTPUT) qDebug() << "Replacement operation failed"; }
QList<CppQuickFixOperation::Ptr> InsertQtPropertyMembers::match(const CppQuickFixState &state) { const QList<AST *> &path = state.path(); if (path.isEmpty()) return noResult(); AST * const ast = path.last(); QtPropertyDeclarationAST *qtPropertyDeclaration = ast->asQtPropertyDeclaration(); if (!qtPropertyDeclaration) return noResult(); ClassSpecifierAST *klass = 0; for (int i = path.size() - 2; i >= 0; --i) { klass = path.at(i)->asClassSpecifier(); if (klass) break; } if (!klass) return noResult(); CppRefactoringChanges refactoring(state.snapshot()); const CppRefactoringFile &file = refactoring.file(state.document()->fileName()); const QString propertyName = file.textOf(qtPropertyDeclaration->property_name); QString getterName; QString setterName; QString signalName; int generateFlags = 0; for (QtPropertyDeclarationItemListAST *it = qtPropertyDeclaration->property_declaration_item_list; it; it = it->next) { const QString tokenString = file.tokenAt(it->value->item_name_token).spell(); if (tokenString == QLatin1String("READ")) { getterName = file.textOf(it->value->expression); generateFlags |= GenerateGetter; } else if (tokenString == QLatin1String("WRITE")) { setterName = file.textOf(it->value->expression); generateFlags |= GenerateSetter; } else if (tokenString == QLatin1String("NOTIFY")) { signalName = file.textOf(it->value->expression); generateFlags |= GenerateSignal; } } QString storageName = QString("m_%1").arg(propertyName); generateFlags |= GenerateStorage; Class *c = klass->symbol; Overview overview; for (unsigned i = 0; i < c->memberCount(); ++i) { Symbol *member = c->memberAt(i); FullySpecifiedType type = member->type(); if (member->asFunction() || (type.isValid() && type->asFunctionType())) { const QString name = overview(member->name()); if (name == getterName) { generateFlags &= ~GenerateGetter; } else if (name == setterName) { generateFlags &= ~GenerateSetter; } else if (name == signalName) { generateFlags &= ~GenerateSignal; } } else if (member->asDeclaration()) { const QString name = overview(member->name()); if (name == storageName) generateFlags &= ~GenerateStorage; } } if (getterName.isEmpty() && setterName.isEmpty() && signalName.isEmpty()) return noResult(); return singleResult(new Operation(state, path.size() - 1, qtPropertyDeclaration, c, generateFlags, getterName, setterName, signalName, storageName)); }