aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/cppeditor
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2024-03-14 10:24:46 +0100
committerChristian Kandeler <christian.kandeler@qt.io>2024-04-09 08:18:30 +0000
commit42edb0dd61a05702370a02d3efcbd5ba32e05e6b (patch)
treeb7bc939e01bb51180e8ccbabb3415841d3cc93d0 /src/plugins/cppeditor
parentdd876dc4054b06638567afbdc3a849d56e99669d (diff)
CppEditor: Make renameIncludes() also work for moved files
Task-number: QTCREATORBUG-26545 Change-Id: I0bfe203af8f091562cdd91411dbe502fc5a76956 Reviewed-by: David Schulz <david.schulz@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'src/plugins/cppeditor')
-rw-r--r--src/plugins/cppeditor/cppmodelmanager.cpp29
-rw-r--r--src/plugins/cppeditor/cppmodelmanager_test.cpp99
-rw-r--r--src/plugins/cppeditor/cppmodelmanager_test.h1
3 files changed, 87 insertions, 42 deletions
diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp
index 5ea954add4..d5d6842e00 100644
--- a/src/plugins/cppeditor/cppmodelmanager.cpp
+++ b/src/plugins/cppeditor/cppmodelmanager.cpp
@@ -1878,16 +1878,15 @@ void CppModelManager::renameIncludes(const QList<std::pair<FilePath, FilePath>>
if (oldFilePath.isEmpty() || newFilePath.isEmpty())
continue;
- // We just want to handle renamings so return when the file was actually moved.
- if (oldFilePath.absolutePath() != newFilePath.absolutePath())
- continue;
-
const TextEditor::PlainRefactoringFileFactory changes;
QString oldFileName = oldFilePath.fileName();
QString newFileName = newFilePath.fileName();
const bool isUiFile = oldFilePath.suffix() == "ui" && newFilePath.suffix() == "ui";
+ const bool moved = oldFilePath.absolutePath() != newFilePath.absolutePath();
if (isUiFile) {
+ if (moved)
+ return; // This is out of scope.
oldFileName = "ui_" + oldFilePath.baseName() + ".h";
newFileName = "ui_" + newFilePath.baseName() + ".h";
}
@@ -1925,12 +1924,26 @@ void CppModelManager::renameIncludes(const QList<std::pair<FilePath, FilePath>>
TextEditor::RefactoringFilePtr file = changes.file(includingFileNew);
const QTextBlock &block = file->document()->findBlockByNumber(loc.second - 1);
- const int replaceStart = block.text().indexOf(oldFileName);
- if (replaceStart > -1) {
+ const FilePath relPathOld = FilePath::fromString(FilePath::calcRelativePath(
+ oldFilePath.toString(), includingFileOld.parentDir().toString()));
+ const FilePath relPathNew = FilePath::fromString(FilePath::calcRelativePath(
+ newFilePath.toString(), includingFileNew.parentDir().toString()));
+ int replaceStart = block.text().indexOf(relPathOld.toString());
+ QString oldString;
+ QString newString;
+ if (isUiFile || replaceStart == -1) {
+ replaceStart = block.text().indexOf(oldFileName);
+ oldString = oldFileName;
+ newString = newFileName;
+ } else {
+ oldString = relPathOld.toString();
+ newString = relPathNew.toString();
+ }
+ if (replaceStart > -1 && oldString != newString) {
ChangeSet changeSet;
changeSet.replace(block.position() + replaceStart,
- block.position() + replaceStart + oldFileName.length(),
- newFileName);
+ block.position() + replaceStart + oldString.length(),
+ newString);
file->setChangeSet(changeSet);
file->apply();
}
diff --git a/src/plugins/cppeditor/cppmodelmanager_test.cpp b/src/plugins/cppeditor/cppmodelmanager_test.cpp
index a896563a61..4ac39de737 100644
--- a/src/plugins/cppeditor/cppmodelmanager_test.cpp
+++ b/src/plugins/cppeditor/cppmodelmanager_test.cpp
@@ -17,6 +17,7 @@
#include <cplusplus/LookupContext.h>
+#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
@@ -965,50 +966,80 @@ void ModelManagerTest::testUpdateEditorsAfterProjectUpdate()
QCOMPARE(documentBProjectPart->topLevelProject, pi->projectFilePath());
}
-void ModelManagerTest::testRenameIncludes()
+void ModelManagerTest::testRenameIncludes_data()
{
- struct ModelManagerGCHelper {
- ~ModelManagerGCHelper() { CppModelManager::GC(); }
- } GCHelper;
- Q_UNUSED(GCHelper) // do not warn about being unused
+ QTest::addColumn<QString>("oldRelPath");
+ QTest::addColumn<QString>("newRelPath");
+ QTest::addColumn<bool>("successExpected");
+
+ QTest::addRow("rename in place 1")
+ << "subdir1/header1.h" << "subdir1/header1_renamed.h" << true;
+ QTest::addRow("rename in place 2")
+ << "subdir2/header2.h" << "subdir2/header2_renamed.h" << true;
+ QTest::addRow("rename in place 3") << "header.h" << "header_renamed.h" << true;
+ QTest::addRow("move up") << "subdir1/header1.h" << "header1_moved.h" << true;
+ QTest::addRow("move up (breaks build)") << "subdir2/header2.h" << "header2_moved.h" << false;
+ QTest::addRow("move down") << "header.h" << "subdir1/header_moved.h" << true;
+ QTest::addRow("move across") << "subdir1/header1.h" << "subdir2/header1_moved.h" << true;
+ QTest::addRow("move across (breaks build)")
+ << "subdir2/header2.h" << "subdir1/header2_moved.h" << false;
+}
+void ModelManagerTest::testRenameIncludes()
+{
+ // Set up project.
TemporaryDir tmpDir;
QVERIFY(tmpDir.isValid());
-
- const QDir workingDir(tmpDir.path());
- const QStringList fileNames = {"foo.h", "foo.cpp", "main.cpp"};
- const FilePath oldHeader = FilePath::fromString(workingDir.filePath("foo.h"));
- const FilePath newHeader = FilePath::fromString(workingDir.filePath("bar.h"));
- const MyTestDataDir testDir(_("testdata_project1"));
-
- // Copy test files to a temporary directory
- QSet<FilePath> sourceFiles;
- for (const QString &fileName : std::as_const(fileNames)) {
- const QString &file = workingDir.filePath(fileName);
- QVERIFY(QFile::copy(testDir.file(fileName), file));
- // Saving source file names for the model manager update,
- // so we can update just the relevant files.
- if (ProjectFile::classify(file) == ProjectFile::CXXSource)
- sourceFiles.insert(FilePath::fromString(file));
- }
-
- // Update the c++ model manager and check for the old includes
- CppModelManager::updateSourceFiles(sourceFiles).waitForFinished();
- QCoreApplication::processEvents();
+ const MyTestDataDir sourceDir("testdata_renameheaders");
+ const FilePath srcFilePath = FilePath::fromString(sourceDir.path());
+ const FilePath projectDir = tmpDir.filePath().pathAppended(srcFilePath.fileName());
+ const auto copyResult = srcFilePath.copyRecursively(projectDir);
+ if (!copyResult)
+ qDebug() << copyResult.error();
+ QVERIFY(copyResult);
+ Kit * const kit = Utils::findOr(KitManager::kits(), nullptr, [](const Kit *k) {
+ return k->isValid() && !k->hasWarning() && k->value("QtSupport.QtInformation").isValid();
+ });
+ if (!kit)
+ QSKIP("The test requires at least one valid kit with a valid Qt");
+ const FilePath projectFile = projectDir.pathAppended(projectDir.fileName() + ".pro");
+ ProjectOpenerAndCloser projectMgr;
+ QVERIFY(projectMgr.open(projectFile, true, kit));
+
+ // Verify initial code model state.
+ const auto makeAbs = [&](const QStringList &relPaths) {
+ return Utils::transform<QSet<FilePath>>(relPaths, [&](const QString &relPath) {
+ return projectDir.pathAppended(relPath);
+ });
+ };
+ const QSet<FilePath> allSources = makeAbs({"main.cpp", "subdir1/file1.cpp", "subdir2/file2.cpp"});
+ const QSet<FilePath> allHeaders = makeAbs({"header.h", "subdir1/header1.h", "subdir2/header2.h"});
CPlusPlus::Snapshot snapshot = CppModelManager::snapshot();
- for (const FilePath &sourceFile : std::as_const(sourceFiles)) {
- QCOMPARE(snapshot.allIncludesForDocument(sourceFile), QSet<FilePath>{oldHeader});
+ for (const FilePath &srcFile : allSources) {
+ QCOMPARE(snapshot.allIncludesForDocument(srcFile), allHeaders);
}
- // Renaming the header
+ // Rename the header.
+ QFETCH(QString, oldRelPath);
+ QFETCH(QString, newRelPath);
+ QFETCH(bool, successExpected);
+ const FilePath oldHeader = projectDir.pathAppended(oldRelPath);
+ const FilePath newHeader = projectDir.pathAppended(newRelPath);
QVERIFY(ProjectExplorerPlugin::renameFile(oldHeader, newHeader));
- // Update the c++ model manager again and check for the new includes
- CppModelManager::updateSourceFiles(sourceFiles).waitForFinished();
- QCoreApplication::processEvents();
+ // Verify new code model state.
+ QVERIFY(::CppEditor::Tests::waitForSignalOrTimeout(
+ CppModelManager::instance(), &CppModelManager::sourceFilesRefreshed, 10000));
+ QSet<FilePath> incompleteNewHeadersSet = allHeaders;
+ incompleteNewHeadersSet.remove(oldHeader);
+ QSet<FilePath> completeNewHeadersSet = incompleteNewHeadersSet;
+ completeNewHeadersSet << newHeader;
+
snapshot = CppModelManager::snapshot();
- for (const FilePath &sourceFile : std::as_const(sourceFiles)) {
- QCOMPARE(snapshot.allIncludesForDocument(sourceFile), QSet<FilePath>{newHeader});
+ for (const FilePath &srcFile : allSources) {
+ const QSet<FilePath> &expectedHeaders = srcFile.fileName() == "main.cpp" && !successExpected
+ ? incompleteNewHeadersSet : completeNewHeadersSet;
+ QCOMPARE(snapshot.allIncludesForDocument(srcFile), expectedHeaders);
}
}
diff --git a/src/plugins/cppeditor/cppmodelmanager_test.h b/src/plugins/cppeditor/cppmodelmanager_test.h
index 26cbefd1c8..14e9c617de 100644
--- a/src/plugins/cppeditor/cppmodelmanager_test.h
+++ b/src/plugins/cppeditor/cppmodelmanager_test.h
@@ -28,6 +28,7 @@ private slots:
void testDefinesPerEditor();
void testUpdateEditorsAfterProjectUpdate();
void testPrecompiledHeaders();
+ void testRenameIncludes_data();
void testRenameIncludes();
void testRenameIncludesInEditor();
void testDocumentsAndRevisions();