in src/checks/level2/old-style-connect.cpp [316:504]
std::vector<FixItHint> OldStyleConnect::fixits(int classification, T *callOrCtor)
{
if (!callOrCtor) {
llvm::errs() << "Call is invalid\n";
return {};
}
const SourceLocation locStart = callOrCtor->getBeginLoc();
if (classification & ConnectFlag_2ArgsDisconnect) {
// Not implemented yet
std::string msg = "Fix it not implemented for disconnect with 2 args";
queueManualFixitWarning(locStart, msg);
return {};
}
if (classification & ConnectFlag_3ArgsDisconnect) {
// Not implemented yet
std::string msg = "Fix it not implemented for disconnect with 3 args";
queueManualFixitWarning(locStart, msg);
return {};
}
if (classification & ConnectFlag_QMessageBoxOpen) {
std::string msg = "Fix it not implemented for QMessageBox::open()";
queueManualFixitWarning(locStart, msg);
return {};
}
std::vector<FixItHint> fixits;
int macroNum = 0;
std::string implicitCallee;
std::string macroName;
CXXMethodDecl *senderMethod = nullptr;
for (auto arg : callOrCtor->arguments()) {
SourceLocation s = arg->getBeginLoc();
static const CXXRecordDecl *lastRecordDecl = nullptr;
if (isSignalOrSlot(s, macroName)) {
macroNum++;
if (!lastRecordDecl && (classification & ConnectFlag_4ArgsConnect)) {
// This means it's a connect with implicit receiver
lastRecordDecl = Utils::recordForMemberCall(dyn_cast<CXXMemberCallExpr>(callOrCtor), implicitCallee);
if (macroNum == 1) {
llvm::errs() << "This first macro shouldn't enter this path";
}
if (!lastRecordDecl) {
std::string msg = "Failed to get class name for implicit receiver";
queueManualFixitWarning(s, msg);
return {};
}
}
if (!lastRecordDecl) {
std::string msg = "Failed to get class name for explicit receiver";
queueManualFixitWarning(s, msg);
return {};
}
const std::string methodName = signalOrSlotNameFromMacro(s);
auto methods = Utils::methodsFromString(lastRecordDecl, methodName);
if (methods.empty()) {
std::string msg;
if (isPrivateSlot(methodName)) {
msg = "Converting Q_PRIVATE_SLOTS not implemented yet\n";
} else {
if (m_context->isQtDeveloper() && classIsOk(clazy::name(lastRecordDecl))) {
// This is OK
return {};
}
msg = "No such method " + methodName + " in class " + lastRecordDecl->getNameAsString();
}
queueManualFixitWarning(s, msg);
return {};
}
if (methods.size() != 1) {
std::string msg = std::string("Too many overloads (") + std::to_string(methods.size()) + std::string(") for method ") + methodName
+ " for record " + lastRecordDecl->getNameAsString();
queueManualFixitWarning(s, msg);
return {};
} else {
AccessSpecifierManager *a = m_context->accessSpecifierManager;
if (!a) {
return {};
}
const bool isSignal = a->qtAccessSpecifierType(methods[0]) == QtAccessSpecifier_Signal;
if (isSignal && macroName == "SLOT") {
// The method is actually a signal and the user used SLOT()
// bail out with the fixing.
std::string msg = std::string("Can't fix. SLOT macro used but method " + methodName + " is a signal");
queueManualFixitWarning(s, msg);
return {};
}
}
auto *methodDecl = methods[0];
if (methodDecl->isStatic()) {
return {};
}
if (macroNum == 1) {
// Save the number of parameters of the signal. The slot should not have more arguments.
senderMethod = methodDecl;
} else if (macroNum == 2) {
const unsigned int numReceiverParams = methodDecl->getNumParams();
if (numReceiverParams > senderMethod->getNumParams()) {
std::string msg = std::string("Receiver has more parameters (") + std::to_string(methodDecl->getNumParams()) + ") than signal ("
+ std::to_string(senderMethod->getNumParams()) + ')';
queueManualFixitWarning(s, msg);
return {};
}
for (unsigned int i = 0; i < numReceiverParams; ++i) {
ParmVarDecl *receiverParm = methodDecl->getParamDecl(i);
ParmVarDecl *senderParm = senderMethod->getParamDecl(i);
if (!clazy::isConvertibleTo(senderParm->getType().getTypePtr(), receiverParm->getType().getTypePtrOrNull())) {
std::string msg("Sender's parameters are incompatible with the receiver's");
queueManualFixitWarning(s, msg);
return {};
}
}
}
if ((classification & ConnectFlag_QTimerSingleShot) && methodDecl->getNumParams() > 0) {
std::string msg = "(QTimer) Fixit not implemented for slot with arguments, use a lambda";
queueManualFixitWarning(s, msg);
return {};
}
if ((classification & ConnectFlag_QMenuAddAction) && methodDecl->getNumParams() > 0) {
std::string msg = "(QMenu) Fixit not implemented for slot with arguments, use a lambda";
queueManualFixitWarning(s, msg);
return {};
}
DeclContext *context = m_context->lastDecl->getDeclContext();
bool isSpecialProtectedCase = false;
if (!clazy::canTakeAddressOf(methodDecl, context, /*by-ref*/ isSpecialProtectedCase)) {
std::string msg = "Can't fix " + clazy::accessString(methodDecl->getAccess()) + ' ' + macroName + ' ' + methodDecl->getQualifiedNameAsString();
queueManualFixitWarning(s, msg);
return {};
}
std::string qualifiedName;
auto *contextRecord = clazy::firstContextOfType<CXXRecordDecl>(m_context->lastDecl->getDeclContext());
const bool isInInclude = sm().getMainFileID() != sm().getFileID(locStart);
if (isSpecialProtectedCase && contextRecord) {
// We're inside a derived class trying to take address of a protected base member, must use &Derived::method instead of &Base::method.
qualifiedName = contextRecord->getNameAsString() + "::" + methodDecl->getNameAsString();
} else {
qualifiedName = clazy::getMostNeededQualifiedName(sm(), methodDecl, context, locStart, !isInInclude); // (In includes ignore using directives)
}
CharSourceRange expansionRange = sm().getImmediateExpansionRange(s);
SourceRange range = SourceRange(expansionRange.getBegin(), expansionRange.getEnd());
const std::string functionPointer = '&' + qualifiedName;
std::string replacement = functionPointer;
if ((classification & ConnectFlag_4ArgsConnect) && macroNum == 2) {
replacement = implicitCallee + ", " + replacement;
}
fixits.push_back(FixItHint::CreateReplacement(range, replacement));
lastRecordDecl = nullptr;
} else {
Expr *expr = arg;
const auto *const record = clazy::getBestDynamicClassType(expr);
if (record) {
lastRecordDecl = record;
if (isQPointer(expr)) {
auto endLoc = clazy::locForNextToken(&m_astContext, arg->getBeginLoc(), tok::comma);
if (endLoc.isValid()) {
fixits.push_back(FixItHint::CreateInsertion(endLoc, ".data()"));
} else {
queueManualFixitWarning(s, "Can't fix this QPointer case");
return {};
}
}
}
}
}
return fixits;
}