CppEditor: Ensure "inline" specifier

... when creating functions in header files. Otherwise, we will likely
cause linker failures in non-trivial projects.

Fixes: QTCREATORBUG-15052
Change-Id: Ic0fff8779ba924f8b9943ab233a0cda409e73e9d
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Christian Kandeler
2020-07-13 15:44:23 +02:00
parent d47bd61e0c
commit 9fa1286faf
3 changed files with 64 additions and 20 deletions

View File

@@ -108,6 +108,7 @@ private slots:
void test_quickfix_GenerateGetterSetter_onlyGetter(); void test_quickfix_GenerateGetterSetter_onlyGetter();
void test_quickfix_GenerateGetterSetter_onlyGetter_DontPreferGetterWithGet(); void test_quickfix_GenerateGetterSetter_onlyGetter_DontPreferGetterWithGet();
void test_quickfix_GenerateGetterSetter_onlySetter(); void test_quickfix_GenerateGetterSetter_onlySetter();
void test_quickfix_GenerateGetterSetter_onlySetterHeaderFile();
void test_quickfix_GenerateGetterSetter_offerGetterWhenSetterPresent(); void test_quickfix_GenerateGetterSetter_offerGetterWhenSetterPresent();
void test_quickfix_GenerateGetterSetter_offerSetterWhenGetterPresent(); void test_quickfix_GenerateGetterSetter_offerSetterWhenGetterPresent();
void test_quickfix_GenerateGettersSetters_data(); void test_quickfix_GenerateGettersSetters_data();

View File

@@ -2242,6 +2242,31 @@ void CppEditorPlugin::test_quickfix_GenerateGetterSetter_onlySetter()
QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 2); QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 2);
} }
void CppEditorPlugin::test_quickfix_GenerateGetterSetter_onlySetterHeaderFile()
{
QList<QuickFixTestDocument::Ptr> testDocuments;
const QByteArray original =
"class Foo\n"
"{\n"
"public:\n"
" int bar@;\n"
"};\n";
const QByteArray expected =
"class Foo\n"
"{\n"
"public:\n"
" int bar@;\n"
" void setBar(int value);\n"
"};\n\n"
"inline void Foo::setBar(int value)\n"
"{\n"
" bar = value;\n"
"}\n";
testDocuments << QuickFixTestDocument::create("file.h", original, expected);
GenerateGetterSetter factory;
QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 2);
}
class CppCodeStyleSettingsChanger { class CppCodeStyleSettingsChanger {
public: public:
CppCodeStyleSettingsChanger(const CppCodeStyleSettings &settings); CppCodeStyleSettingsChanger(const CppCodeStyleSettings &settings);
@@ -2457,17 +2482,17 @@ private:
int bar2_; int bar2_;
QString bar3; QString bar3;
}; };
void Foo::setBar2(int bar2) inline void Foo::setBar2(int bar2)
{ {
bar2_ = bar2; bar2_ = bar2;
} }
QString Foo::getBar3() const inline QString Foo::getBar3() const
{ {
return bar3; return bar3;
} }
void Foo::setBar3(const QString &value) inline void Foo::setBar3(const QString &value)
{ {
bar3 = value; bar3 = value;
} }
@@ -2513,7 +2538,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_afterClass()
" void a();\n" " void a();\n"
"};\n" "};\n"
"\n" "\n"
"void Foo::a()\n" "inline void Foo::a()\n"
"{\n\n}\n" "{\n\n}\n"
"\n" "\n"
"class Bar {};\n"; "class Bar {};\n";
@@ -4422,7 +4447,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutside2()
" void f3();\n" " void f3();\n"
"};\n" "};\n"
"\n" "\n"
"int Foo::f2()\n" "inline int Foo::f2()\n"
"{\n" "{\n"
" return 1;\n" " return 1;\n"
"}\n"; "}\n";
@@ -4728,7 +4753,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_afterClass()
" void a();\n" " void a();\n"
"};\n" "};\n"
"\n" "\n"
"void Foo::a() {}\n" "inline void Foo::a() {}\n"
"\n" "\n"
"class Bar {};\n"; "class Bar {};\n";
testDocuments << QuickFixTestDocument::create("file.h", original, expected); testDocuments << QuickFixTestDocument::create("file.h", original, expected);
@@ -5017,7 +5042,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_FreeFuncToCpp()
// Header File // Header File
original = "int number() const;\n"; original = "int number() const;\n";
expected = expected =
"int number() const\n" "inline int number() const\n"
"{\n" "{\n"
" return 5;\n" " return 5;\n"
"}\n"; "}\n";
@@ -5053,7 +5078,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_FreeFuncToCppNS()
"}\n"; "}\n";
expected = expected =
"namespace MyNamespace {\n" "namespace MyNamespace {\n"
"int number() const\n" "inline int number() const\n"
"{\n" "{\n"
" return 5;\n" " return 5;\n"
"}\n" "}\n"
@@ -5442,7 +5467,7 @@ void CppEditorPlugin::test_quickfix_ExtractFunction_data()
"{\n" "{\n"
" @{start}g();@{end}\n" " @{start}g();@{end}\n"
"}\n") "}\n")
<< _("void extracted()\n" << _("inline void extracted()\n"
"{\n" "{\n"
" g();\n" " g();\n"
"}\n" "}\n"
@@ -5469,7 +5494,7 @@ void CppEditorPlugin::test_quickfix_ExtractFunction_data()
"private:\n" "private:\n"
" void bar();\n" " void bar();\n"
"};\n\n" "};\n\n"
"void Foo::extracted()\n" "inline void Foo::extracted()\n"
"{\n" "{\n"
" g();\n" " g();\n"
"}\n\n" "}\n\n"
@@ -5496,7 +5521,7 @@ void CppEditorPlugin::test_quickfix_ExtractFunction_data()
" void extracted(NS::C &c);\n" // TODO: Remove non-required qualification " void extracted(NS::C &c);\n" // TODO: Remove non-required qualification
"};\n" "};\n"
"}\n" "}\n"
"void NS::C::extracted(NS::C &c)\n" "inline void NS::C::extracted(NS::C &c)\n"
"{\n" "{\n"
" C *c = &c;\n" " C *c = &c;\n"
"}\n" "}\n"

View File

@@ -114,6 +114,15 @@ const QList<CppQuickFixFactory *> &CppQuickFixFactory::cppQuickFixFactories()
namespace Internal { namespace Internal {
QString inlinePrefix(const QString &targetFile, const std::function<bool()> &extraCondition = {})
{
if (ProjectFile::isHeader(ProjectFile::classify(targetFile))
&& (!extraCondition || extraCondition())) {
return "inline ";
}
return {};
}
// In the following anonymous namespace all functions are collected, which could be of interest for // In the following anonymous namespace all functions are collected, which could be of interest for
// different quick fixes. // different quick fixes.
namespace { namespace {
@@ -2831,8 +2840,10 @@ public:
} }
const QString name = oo.prettyName(LookupContext::minimalName(m_decl, targetCoN, const QString name = oo.prettyName(LookupContext::minimalName(m_decl, targetCoN,
control)); control));
const QString defText = inlinePrefix(
const QString defText = oo.prettyType(tn, name) + QLatin1String("\n{\n\n}"); m_targetFileName, [this] { return m_defpos == DefPosOutsideClass; })
+ oo.prettyType(tn, name)
+ QLatin1String("\n{\n\n}");
const int targetPos = targetFile->position(m_loc.line(), m_loc.column()); const int targetPos = targetFile->position(m_loc.line(), m_loc.column());
const int targetPos2 = qMax(0, targetFile->position(m_loc.line(), 1) - 1); const int targetPos2 = qMax(0, targetFile->position(m_loc.line(), 1) - 1);
@@ -3290,14 +3301,15 @@ public:
// Construct implementation strings // Construct implementation strings
const QString implementationGetterTypeAndNameString = oo.prettyType( const QString implementationGetterTypeAndNameString = oo.prettyType(
getterType, QString::fromLatin1("%1::%2").arg(classString, getterName)); getterType, QString::fromLatin1("%1::%2").arg(classString, getterName));
const QString implementationGetter = QString::fromLatin1("%1()%2\n" const QString inlineSpecifier = sameFile && wasHeader ? QString("inline") : QString();
const QString implementationGetter = QString::fromLatin1("%4 %1()%2\n"
"{\n" "{\n"
"return %3;\n" "return %3;\n"
"}") "}")
.arg(implementationGetterTypeAndNameString, .arg(implementationGetterTypeAndNameString,
isStatic ? QString() : QLatin1String(" const"), isStatic ? QString() : QLatin1String(" const"),
rawName); rawName, inlineSpecifier);
const QString implementationSetter = QString::fromLatin1("void %1::%2(%3)\n" const QString implementationSetter = QString::fromLatin1("%6 void %1::%2(%3)\n"
"{\n" "{\n"
"%4 = %5;\n" "%4 = %5;\n"
"}") "}")
@@ -3305,7 +3317,8 @@ public:
setterName, setterName,
paramString, paramString,
rawName, rawName,
paramName); paramName,
inlineSpecifier);
QString implementation; QString implementation;
if (generateGetter()) if (generateGetter())
@@ -3872,6 +3885,7 @@ public:
} }
funcDef.append(QLatin1String("\n}\n\n")); funcDef.append(QLatin1String("\n}\n\n"));
funcDef.replace(QChar::ParagraphSeparator, QLatin1String("\n")); funcDef.replace(QChar::ParagraphSeparator, QLatin1String("\n"));
funcDef.prepend(inlinePrefix(currentFile->fileName()));
funcCall.append(QLatin1Char(';')); funcCall.append(QLatin1Char(';'));
// Get starting indentation from original code. // Get starting indentation from original code.
@@ -5288,7 +5302,9 @@ public:
Scope *scopeAtInsertPos = m_toFile->cppDocument()->scopeAt(l.line(), l.column()); Scope *scopeAtInsertPos = m_toFile->cppDocument()->scopeAt(l.line(), l.column());
// construct definition // construct definition
const QString funcDec = definitionSignature(m_operation, funcAST, m_fromFile, m_toFile, const QString funcDec = inlinePrefix(
m_toFile->fileName(), [this] { return m_type == MoveOutside; })
+ definitionSignature(m_operation, funcAST, m_fromFile, m_toFile,
scopeAtInsertPos); scopeAtInsertPos);
QString funcDef = prefix + funcDec; QString funcDef = prefix + funcDec;
const int startPosition = m_fromFile->endOf(funcAST->declarator); const int startPosition = m_fromFile->endOf(funcAST->declarator);
@@ -5686,10 +5702,12 @@ void MoveFuncDefToDecl::match(const CppQuickFixInterface &interface, QuickFixOpe
} }
} }
if (!declText.isEmpty()) if (!declText.isEmpty()) {
declText.prepend(inlinePrefix(declFileName));
break; break;
} }
} }
}
if (!declFileName.isEmpty() && !declText.isEmpty()) if (!declFileName.isEmpty() && !declText.isEmpty())
result << new MoveFuncDefToDeclOp(interface, result << new MoveFuncDefToDeclOp(interface,