diff --git a/src/libs/utils/fancymainwindow.cpp b/src/libs/utils/fancymainwindow.cpp
index 4fc91537da7..fd315a8e117 100644
--- a/src/libs/utils/fancymainwindow.cpp
+++ b/src/libs/utils/fancymainwindow.cpp
@@ -153,13 +153,16 @@ public:
m_floatButton = new DockWidgetTitleButton(this);
m_floatButton->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarNormalButton, &opt, q));
- m_floatButton->setAccessibleName(QDockWidget::tr("Float"));
- m_floatButton->setAccessibleDescription(QDockWidget::tr("Undocks and re-attaches the dock widget"));
m_closeButton = new DockWidgetTitleButton(this);
m_closeButton->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton, &opt, q));
+
+#ifndef QT_NO_ACCESSIBILITY
+ m_floatButton->setAccessibleName(QDockWidget::tr("Float"));
+ m_floatButton->setAccessibleDescription(QDockWidget::tr("Undocks and re-attaches the dock widget"));
m_closeButton->setAccessibleName(QDockWidget::tr("Close"));
m_closeButton->setAccessibleDescription(QDockWidget::tr("Closes the dock widget"));
+#endif
setActive(false);
diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp
index d19c12035df..5955d351f0e 100644
--- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp
+++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp
@@ -613,7 +613,7 @@ void ClangCompletionAssistProcessor::handleAvailableCompletions(
QTC_CHECK(m_completions.isEmpty());
m_completions = toAssistProposalItems(completions);
- if (m_addSnippets)
+ if (m_addSnippets && !m_completions.isEmpty())
addSnippets();
setAsyncProposalAvailable(createProposal(neededCorrection));
diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp
index 54f35b9dca4..2714c254b91 100644
--- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp
+++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp
@@ -932,18 +932,28 @@ void ClangCodeCompletionTest::testCompleteConstructorAndFallbackToGlobalCompleti
QVERIFY(!hasSnippet(t.proposal, "class"));
}
+// Explicitly Inserting The Dot
+// ----------------------------
+// Inserting the dot for is important since it will send the editor
+// content to the backend and thus generate an unsaved file on the backend
+// side. The unsaved file enables us to do the dot to arrow correction.
+
void ClangCodeCompletionTest::testCompleteWithDotToArrowCorrection()
{
- // Inserting the dot for this test is important since it will send the editor
- // content to the backend and thus generate an unsaved file on the backend
- // side. The unsaved file enables us to do the dot to arrow correction.
-
ProjectLessCompletionTest t("dotToArrowCorrection.cpp",
- QStringLiteral("."));
+ QStringLiteral(".")); // See above "Explicitly Inserting The Dot"
QVERIFY(hasItem(t.proposal, "member"));
}
+void ClangCodeCompletionTest::testDontCompleteWithDotToArrowCorrectionForFloats()
+{
+ ProjectLessCompletionTest t("noDotToArrowCorrectionForFloats.cpp",
+ QStringLiteral(".")); // See above "Explicitly Inserting The Dot"
+
+ QCOMPARE(t.proposal->size(), 0);
+}
+
void ClangCodeCompletionTest::testCompleteProjectDependingCode()
{
const TestDocument testDocument("completionWithProject.cpp");
diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.h b/src/plugins/clangcodemodel/test/clangcodecompletion_test.h
index bd9151bcac4..3f163c62663 100644
--- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.h
+++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.h
@@ -48,6 +48,7 @@ private slots:
void testCompleteConstructorAndFallbackToGlobalCompletion();
void testCompleteWithDotToArrowCorrection();
+ void testDontCompleteWithDotToArrowCorrectionForFloats();
void testCompleteProjectDependingCode();
void testCompleteProjectDependingCodeAfterChangingProject();
diff --git a/src/plugins/clangcodemodel/test/data/clangtestdata.qrc b/src/plugins/clangcodemodel/test/data/clangtestdata.qrc
index c013ccd7f40..ad5ccb47681 100644
--- a/src/plugins/clangcodemodel/test/data/clangtestdata.qrc
+++ b/src/plugins/clangcodemodel/test/data/clangtestdata.qrc
@@ -22,5 +22,6 @@
objc_messages_3.mm
preprocessorKeywordsCompletion.cpp
dotToArrowCorrection.cpp
+ noDotToArrowCorrectionForFloats.cpp
diff --git a/src/plugins/clangcodemodel/test/data/noDotToArrowCorrectionForFloats.cpp b/src/plugins/clangcodemodel/test/data/noDotToArrowCorrectionForFloats.cpp
new file mode 100644
index 00000000000..7b29c3e171d
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/data/noDotToArrowCorrectionForFloats.cpp
@@ -0,0 +1,4 @@
+void f()
+{
+ 0 /* COMPLETE HERE */
+}
diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h
index 4f258e6d239..53337230d8f 100644
--- a/src/plugins/projectexplorer/runconfiguration.h
+++ b/src/plugins/projectexplorer/runconfiguration.h
@@ -173,6 +173,8 @@ class PROJECTEXPLORER_EXPORT Runnable
bool canReUseOutputPane(const std::unique_ptr &other) const override
{
+ if (!other.get())
+ return false;
if (other->typeId() != typeId())
return false;
auto that = static_cast *>(other.get());
diff --git a/src/tools/clangbackend/ipcsource/codecompleter.cpp b/src/tools/clangbackend/ipcsource/codecompleter.cpp
index 436c6680524..4344a22aa93 100644
--- a/src/tools/clangbackend/ipcsource/codecompleter.cpp
+++ b/src/tools/clangbackend/ipcsource/codecompleter.cpp
@@ -68,8 +68,7 @@ CodeCompletions CodeCompleter::complete(uint line, uint column)
translationUnit.cxUnsavedFiles(),
translationUnit.unsavedFilesCount());
- if (results.hasNoResultsForDotCompletion() && hasDotAt(line, column - 1))
- results = completeWithArrowInsteadOfDot(line, column);
+ tryDotArrowCorrectionIfNoResults(results, line, column);
return toCodeCompletions(results);
}
@@ -93,13 +92,6 @@ ClangCodeCompleteResults CodeCompleter::complete(uint line,
defaultOptions());
}
-bool CodeCompleter::hasDotAt(uint line, uint column) const
-{
- const UnsavedFile &unsavedFile = translationUnit.unsavedFile();
-
- return unsavedFile.hasCharacterAt(line, column, '.');
-}
-
uint CodeCompleter::defaultOptions() const
{
uint options = CXCodeComplete_IncludeMacros
@@ -111,12 +103,25 @@ uint CodeCompleter::defaultOptions() const
return options;
}
-ClangCodeCompleteResults CodeCompleter::completeWithArrowInsteadOfDot(uint line, uint column)
+void CodeCompleter::tryDotArrowCorrectionIfNoResults(ClangCodeCompleteResults &results,
+ uint line,
+ uint column)
+{
+ if (results.hasNoResultsForDotCompletion()) {
+ const UnsavedFile &unsavedFile = translationUnit.unsavedFile();
+ bool positionIsOk = false;
+ const uint dotPosition = unsavedFile.toUtf8Position(line, column - 1, &positionIsOk);
+ if (positionIsOk && unsavedFile.hasCharacterAt(dotPosition, '.'))
+ results = completeWithArrowInsteadOfDot(line, column, dotPosition);
+ }
+}
+
+ClangCodeCompleteResults CodeCompleter::completeWithArrowInsteadOfDot(uint line,
+ uint column,
+ uint dotPosition)
{
ClangCodeCompleteResults results;
-
- const SourceLocation location = translationUnit.sourceLocationAtWithoutReparsing(line, column - 1);
- const bool replaced = translationUnit.unsavedFile().replaceAt(location.offset(),
+ const bool replaced = translationUnit.unsavedFile().replaceAt(dotPosition,
1,
Utf8StringLiteral("->"));
diff --git a/src/tools/clangbackend/ipcsource/codecompleter.h b/src/tools/clangbackend/ipcsource/codecompleter.h
index 313323a9d93..c859b2d02e2 100644
--- a/src/tools/clangbackend/ipcsource/codecompleter.h
+++ b/src/tools/clangbackend/ipcsource/codecompleter.h
@@ -45,18 +45,20 @@ public:
CompletionCorrection neededCorrection() const;
-public: // for tests
- bool hasDotAt(uint line, uint column) const;
-
private:
uint defaultOptions() const;
+ void tryDotArrowCorrectionIfNoResults(ClangCodeCompleteResults &results,
+ uint line,
+ uint column);
+
ClangCodeCompleteResults complete(uint line,
uint column,
CXUnsavedFile *unsavedFiles,
unsigned unsavedFileCount);
-
- ClangCodeCompleteResults completeWithArrowInsteadOfDot(uint line, uint column);
+ ClangCodeCompleteResults completeWithArrowInsteadOfDot(uint line,
+ uint column,
+ uint dotPosition);
Utf8String filePath() const;
static void checkCodeCompleteResult(CXCodeCompleteResults *completeResults);
diff --git a/src/tools/clangbackend/ipcsource/unsavedfile.cpp b/src/tools/clangbackend/ipcsource/unsavedfile.cpp
index cc6e0296025..16d6638e8a5 100644
--- a/src/tools/clangbackend/ipcsource/unsavedfile.cpp
+++ b/src/tools/clangbackend/ipcsource/unsavedfile.cpp
@@ -71,15 +71,24 @@ const char *UnsavedFile::filePath() const
return cxUnsavedFile.Filename;
}
-bool UnsavedFile::hasCharacterAt(uint line, uint column, char character) const
+uint UnsavedFile::toUtf8Position(uint line, uint column, bool *ok) const
{
Utf8PositionFromLineColumn converter(cxUnsavedFile.Contents);
if (converter.find(line, column)) {
- const uint utf8Position = converter.position();
- return hasCharacterAt(utf8Position, character);
+ *ok = true;
+ return converter.position();
}
- return false;
+ *ok = false;
+ return 0;
+}
+
+bool UnsavedFile::hasCharacterAt(uint line, uint column, char character) const
+{
+ bool positionIsOk = false;
+ const uint utf8Position = toUtf8Position(line, column, &positionIsOk);
+
+ return positionIsOk && hasCharacterAt(utf8Position, character);
}
bool UnsavedFile::hasCharacterAt(uint position, char character) const
diff --git a/src/tools/clangbackend/ipcsource/unsavedfile.h b/src/tools/clangbackend/ipcsource/unsavedfile.h
index 3433093ae15..dcd4fd026b1 100644
--- a/src/tools/clangbackend/ipcsource/unsavedfile.h
+++ b/src/tools/clangbackend/ipcsource/unsavedfile.h
@@ -55,6 +55,7 @@ public:
const char *filePath() const;
// 1-based line and column
+ uint toUtf8Position(uint line, uint column, bool *ok) const;
bool hasCharacterAt(uint line, uint column, char character) const;
bool hasCharacterAt(uint position, char character) const;
bool replaceAt(uint position, uint length, const Utf8String &replacement);
diff --git a/tests/unit/unittest/codecompletiontest.cpp b/tests/unit/unittest/codecompletiontest.cpp
index a370f935867..c25b03a492a 100644
--- a/tests/unit/unittest/codecompletiontest.cpp
+++ b/tests/unit/unittest/codecompletiontest.cpp
@@ -112,6 +112,19 @@ protected:
readFileContent(QStringLiteral("/complete_withDotArrowCorrectionForPointer.cpp")),
true
};
+ ClangBackEnd::FileContainer dotArrowCorrectionForPointerFileContainerBeforeTyping{
+ Utf8StringLiteral(TESTDATA_DIR"/complete_withDotArrowCorrectionForPointer.cpp"),
+ projectPart.projectPartId(),
+ readFileContent(QStringLiteral("/complete_withDotArrowCorrectionForPointer_beforeTyping.cpp")),
+ true
+ };
+ ClangBackEnd::FileContainer dotArrowCorrectionForPointerFileContainerAfterTyping{
+ Utf8StringLiteral(TESTDATA_DIR"/complete_withDotArrowCorrectionForPointer.cpp"),
+ projectPart.projectPartId(),
+ readFileContent(QStringLiteral("/complete_withDotArrowCorrectionForPointer_afterTyping.cpp")),
+ true
+ };
+
ClangBackEnd::FileContainer dotArrowCorrectionForPointerFileContainerInitial{
Utf8StringLiteral(TESTDATA_DIR"/complete_withDotArrowCorrectionForPointer.cpp"),
projectPart.projectPartId(),
@@ -313,30 +326,6 @@ TEST_F(CodeCompleter, ArrowCompletion)
ClangBackEnd::CompletionCorrection::NoCorrection);
}
-TEST_F(CodeCompleter, HasDotAt)
-{
- auto myCompleter = setupCompleter(dotArrowCorrectionForPointerFileContainer);
-
- ASSERT_TRUE(myCompleter.hasDotAt(5, 8));
-}
-
-TEST_F(CodeCompleter, HasDotAtWithUpdatedUnsavedFile)
-{
- auto myCompleter = setupCompleter(dotArrowCorrectionForPointerFileContainerInitial);
- unsavedFiles.createOrUpdate({dotArrowCorrectionForPointerFileContainerUpdated});
-
- ASSERT_TRUE(myCompleter.hasDotAt(5, 8));
-}
-
-TEST_F(CodeCompleter, HasNoDotAtDueToMissingUnsavedFile)
-{
- const ClangBackEnd::FileContainer fileContainer = dotArrowCorrectionForPointerFileContainer;
- translationUnits.create({fileContainer});
- ClangBackEnd::CodeCompleter myCompleter(translationUnits.translationUnit(fileContainer));
-
- ASSERT_FALSE(myCompleter.hasDotAt(5, 8));
-}
-
TEST_F(CodeCompleter, DotToArrowCompletionForPointer)
{
auto myCompleter = setupCompleter(dotArrowCorrectionForPointerFileContainer);
@@ -350,6 +339,24 @@ TEST_F(CodeCompleter, DotToArrowCompletionForPointer)
ClangBackEnd::CompletionCorrection::DotToArrowCorrection);
}
+TEST_F(CodeCompleter, DotToArrowCompletionForPointerInOutdatedTranslationUnit)
+{
+ auto fileContainerBeforeTyping = dotArrowCorrectionForPointerFileContainerBeforeTyping;
+ auto myCompleter = setupCompleter(fileContainerBeforeTyping);
+ auto translationUnit = translationUnits.translationUnit(fileContainerBeforeTyping.filePath(),
+ fileContainerBeforeTyping.projectPartId());
+ translationUnit.cxTranslationUnit(); // Parse
+ unsavedFiles.createOrUpdate({dotArrowCorrectionForPointerFileContainerAfterTyping});
+
+ const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 9);
+
+ ASSERT_THAT(completions,
+ Contains(IsCodeCompletion(Utf8StringLiteral("member"),
+ CodeCompletion::VariableCompletionKind)));
+ ASSERT_THAT(myCompleter.neededCorrection(),
+ ClangBackEnd::CompletionCorrection::DotToArrowCorrection);
+}
+
TEST_F(CodeCompleter, NoDotToArrowCompletionForObject)
{
auto myCompleter = setupCompleter(noDotArrowCorrectionForObjectFileContainer);
diff --git a/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer_afterTyping.cpp b/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer_afterTyping.cpp
new file mode 100644
index 00000000000..9f8d3645b2e
--- /dev/null
+++ b/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer_afterTyping.cpp
@@ -0,0 +1,6 @@
+struct Foo { int member; };
+
+void g(Foo *foo)
+{
+ foo.
+}
diff --git a/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer_beforeTyping.cpp b/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer_beforeTyping.cpp
new file mode 100644
index 00000000000..0472cfacaa4
--- /dev/null
+++ b/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer_beforeTyping.cpp
@@ -0,0 +1,6 @@
+struct Foo { int member; };
+
+void g(Foo *foo)
+{
+
+}
diff --git a/tests/unit/unittest/unsavedfiletest.cpp b/tests/unit/unittest/unsavedfiletest.cpp
index 3f0ae6d90b1..463c29be8b3 100644
--- a/tests/unit/unittest/unsavedfiletest.cpp
+++ b/tests/unit/unittest/unsavedfiletest.cpp
@@ -34,6 +34,7 @@
using ClangBackEnd::UnsavedFile;
using ClangBackEnd::UnsavedFiles;
+using ::testing::Eq;
using ::testing::PrintToString;
namespace {
@@ -150,6 +151,44 @@ TEST_F(UnsavedFile, Replace)
ASSERT_THAT(unsavedFile, IsUnsavedFile(filePath, expectedContent, expectedContent.byteSize()));
}
+TEST_F(UnsavedFile, ToUtf8PositionForValidLineColumn)
+{
+ ::UnsavedFile unsavedFile(filePath, fileContent);
+ bool ok = false;
+
+ const uint position = unsavedFile.toUtf8Position(1, 1, &ok);
+
+ ASSERT_TRUE(ok);
+ ASSERT_THAT(position, Eq(0));
+}
+
+TEST_F(UnsavedFile, ToUtf8PositionForInValidLineColumn)
+{
+ ::UnsavedFile unsavedFile(filePath, fileContent);
+ bool ok = false;
+
+ unsavedFile.toUtf8Position(2, 1, &ok);
+
+ ASSERT_FALSE(ok);
+}
+
+TEST_F(UnsavedFile, ToUtf8PositionForDefaultConstructedUnsavedFile)
+{
+ ::UnsavedFile unsavedFile;
+ bool ok = false;
+
+ unsavedFile.toUtf8Position(1, 1, &ok);
+
+ ASSERT_FALSE(ok);
+}
+
+TEST_F(UnsavedFile, HasNoCharacterForDefaultConstructedUnsavedFile)
+{
+ ::UnsavedFile unsavedFile;
+
+ ASSERT_FALSE(unsavedFile.hasCharacterAt(0, 'x'));
+}
+
TEST_F(UnsavedFile, HasNoCharacterForTooBigOffset)
{
::UnsavedFile unsavedFile(filePath, fileContent);