yvvan created this revision.
yvvan added reviewers: ilya-biryukov, klimek, bkramer, arphaman.
Follow up for https://reviews.llvm.org/D41537 - libclang part is extracted into
this review
https://reviews.llvm.org/D46862
Files:
include/clang-c/Index.h
test/Index/complete-arrow-dot.cpp
tools/c-index-test/c-index-test.c
tools/libclang/CIndexCodeCompletion.cpp
tools/libclang/libclang.exports
Index: tools/libclang/libclang.exports
===================================================================
--- tools/libclang/libclang.exports
+++ tools/libclang/libclang.exports
@@ -171,6 +171,8 @@
clang_getCompletionChunkCompletionString
clang_getCompletionChunkKind
clang_getCompletionChunkText
+clang_getCompletionNumFixIts
+clang_getCompletionFixIt
clang_getCompletionNumAnnotations
clang_getCompletionParent
clang_getCompletionPriority
Index: tools/libclang/CIndexCodeCompletion.cpp
===================================================================
--- tools/libclang/CIndexCodeCompletion.cpp
+++ tools/libclang/CIndexCodeCompletion.cpp
@@ -16,6 +16,7 @@
#include "CIndexDiagnostic.h"
#include "CLog.h"
#include "CXCursor.h"
+#include "CXSourceLocation.h"
#include "CXString.h"
#include "CXTranslationUnit.h"
#include "clang/AST/Decl.h"
@@ -302,10 +303,53 @@
/// \brief A string containing the Objective-C selector entered thus far for a
/// message send.
std::string Selector;
+
+ /// \brief Vector of fix-its for each completion result that *must* be applied
+ /// before that result for the corresponding completion item.
+ std::vector<std::vector<FixItHint>> FixItsVector;
};
} // end anonymous namespace
+unsigned clang_getCompletionNumFixIts(CXCodeCompleteResults *results,
+ unsigned completion_index) {
+ AllocatedCXCodeCompleteResults *allocated_results = (AllocatedCXCodeCompleteResults *)results;
+
+ if (!allocated_results || allocated_results->FixItsVector.size() <= completion_index)
+ return 0;
+
+ return static_cast<unsigned>(allocated_results->FixItsVector[completion_index].size());
+}
+
+CXString clang_getCompletionFixIt(CXCodeCompleteResults *results,
+ unsigned completion_index,
+ unsigned fixit_index,
+ CXSourceRange *replacement_range) {
+ AllocatedCXCodeCompleteResults *allocated_results = (AllocatedCXCodeCompleteResults *)results;
+
+ if (!allocated_results || allocated_results->FixItsVector.size() <= completion_index) {
+ if (replacement_range)
+ *replacement_range = clang_getNullRange();
+ return cxstring::createNull();
+ }
+
+ ArrayRef<FixItHint> FixIts = allocated_results->FixItsVector[completion_index];
+ if (FixIts.size() <= fixit_index) {
+ if (replacement_range)
+ *replacement_range = clang_getNullRange();
+ return cxstring::createNull();
+ }
+
+ const FixItHint &FixIt = FixIts[fixit_index];
+ if (replacement_range) {
+ *replacement_range = cxloc::translateSourceRange(
+ *allocated_results->SourceMgr, allocated_results->LangOpts,
+ FixIt.RemoveRange);
+ }
+
+ return cxstring::createRef(FixIt.CodeToInsert.c_str());
+}
+
/// \brief Tracks the number of code-completion result objects that are
/// currently active.
///
@@ -531,18 +575,22 @@
CodeCompletionResult *Results,
unsigned NumResults) override {
StoredResults.reserve(StoredResults.size() + NumResults);
+ if (includeFixIts())
+ AllocatedResults.FixItsVector.reserve(NumResults);
for (unsigned I = 0; I != NumResults; ++I) {
- CodeCompletionString *StoredCompletion
+ CodeCompletionString *StoredCompletion
= Results[I].CreateCodeCompletionString(S, Context, getAllocator(),
getCodeCompletionTUInfo(),
includeBriefComments());
CXCompletionResult R;
R.CursorKind = Results[I].CursorKind;
R.CompletionString = StoredCompletion;
StoredResults.push_back(R);
+ if (includeFixIts())
+ AllocatedResults.FixItsVector.emplace_back(std::move(Results[I].FixIts));
}
-
+
enum CodeCompletionContext::Kind contextKind = Context.getKind();
AllocatedResults.ContextKind = contextKind;
@@ -644,13 +692,13 @@
unsigned options) {
bool IncludeBriefComments = options & CXCodeComplete_IncludeBriefComments;
bool SkipPreamble = options & CXCodeComplete_SkipPreamble;
+ bool IncludeFixIts = options & CXCodeComplete_IncludeFixIts;
#ifdef UDP_CODE_COMPLETION_LOGGER
#ifdef UDP_CODE_COMPLETION_LOGGER_PORT
const llvm::TimeRecord &StartTime = llvm::TimeRecord::getCurrentTime();
#endif
#endif
-
bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != nullptr;
if (cxtu::isNotUsableTU(TU)) {
@@ -691,6 +739,7 @@
CodeCompleteOptions Opts;
Opts.IncludeBriefComments = IncludeBriefComments;
Opts.LoadExternal = !SkipPreamble;
+ Opts.IncludeFixIts = IncludeFixIts;
CaptureCompletionResults Capture(Opts, *Results, &TU);
// Perform completion.
@@ -964,7 +1013,7 @@
= (CodeCompletionString *)XR.CompletionString;
CodeCompletionString *Y
= (CodeCompletionString *)YR.CompletionString;
-
+
SmallString<256> XBuffer;
StringRef XText = GetTypedName(X, XBuffer);
SmallString<256> YBuffer;
Index: tools/c-index-test/c-index-test.c
===================================================================
--- tools/c-index-test/c-index-test.c
+++ tools/c-index-test/c-index-test.c
@@ -2268,8 +2268,24 @@
}
-static void print_completion_result(CXCompletionResult *completion_result,
+static void tokens_spelling_at_range(char *result_buffer,
+ CXTranslationUnit translation_unit,
+ CXSourceRange range) {
+ CXToken *tokens;
+ unsigned tokens_number;
+ clang_tokenize(translation_unit, range, &tokens, &tokens_number);
+ for (unsigned j = 0; j < tokens_number; ++j) {
+ strcat(result_buffer, clang_getCString(clang_getTokenSpelling(
+ translation_unit, tokens[j])));
+ }
+ clang_disposeTokens(translation_unit, tokens, tokens_number);
+}
+
+static void print_completion_result(CXTranslationUnit translation_unit,
+ CXCodeCompleteResults *completion_results,
+ unsigned index,
FILE *file) {
+ CXCompletionResult *completion_result = completion_results->Results + index;
CXString ks = clang_getCursorKindSpelling(completion_result->CursorKind);
unsigned annotationCount;
enum CXCursorKind ParentKind;
@@ -2337,7 +2353,20 @@
fprintf(file, "(brief comment: %s)", BriefCommentCString);
}
clang_disposeString(BriefComment);
-
+
+ for (unsigned i = 0;
+ i < clang_getCompletionNumFixIts(completion_results, index); ++i) {
+ CXSourceRange correction_range;
+ CXString FixIt = clang_getCompletionFixIt(completion_results, index, i,
+ &correction_range);
+ char replaced_string[8];
+ tokens_spelling_at_range(replaced_string, translation_unit,
+ correction_range);
+ fprintf(file, " (requires fix: \"%s\" to \"%s\")", replaced_string,
+ clang_getCString(FixIt));
+ clang_disposeString(FixIt);
+ }
+
fprintf(file, "\n");
}
@@ -2436,6 +2465,8 @@
completionOptions |= CXCodeComplete_IncludeBriefComments;
if (getenv("CINDEXTEST_COMPLETION_SKIP_PREAMBLE"))
completionOptions |= CXCodeComplete_SkipPreamble;
+ if (getenv("CINDEXTEST_COMPLETION_INCLUDE_CORRECTIONS"))
+ completionOptions |= CXCodeComplete_IncludeFixIts;
if (timing_only)
input += strlen("-code-completion-timing=");
@@ -2500,7 +2531,7 @@
clang_sortCodeCompletionResults(results->Results, results->NumResults);
for (i = 0; i != n; ++i)
- print_completion_result(results->Results + i, stdout);
+ print_completion_result(TU, results, i, stdout);
}
n = clang_codeCompleteGetNumDiagnostics(results);
for (i = 0; i != n; ++i) {
Index: test/Index/complete-arrow-dot.cpp
===================================================================
--- test/Index/complete-arrow-dot.cpp
+++ test/Index/complete-arrow-dot.cpp
@@ -0,0 +1,54 @@
+struct X {
+ void doSomething();
+
+ int field;
+};
+
+void X::doSomething() {
+ // RUN: c-index-test -code-completion-at=%s:10:8 %s | FileCheck %s
+ // RUN: env CINDEXTEST_COMPLETION_INCLUDE_CORRECTIONS=1 c-index-test -code-completion-at=%s:10:8 %s | FileCheck -check-prefix=CHECK-WITH-CORRECTION %s
+ this.;
+}
+
+void doSomething() {
+ // RUN: c-index-test -code-completion-at=%s:17:6 %s | FileCheck -check-prefix=CHECK-ARROW-TO-DOT %s
+ // RUN: env CINDEXTEST_COMPLETION_INCLUDE_CORRECTIONS=1 c-index-test -code-completion-at=%s:17:6 %s | FileCheck -check-prefix=CHECK-ARROW-TO-DOT-WITH-CORRECTION %s
+ X x;
+ x->
+}
+
+// CHECK-NOT: CXXMethod:{ResultType void}{TypedText doSomething}{LeftParen (}{RightParen )} (34) (requires fix: "." to "->")
+// CHECK-NOT: FieldDecl:{ResultType int}{TypedText field} (35) (requires fix: "." to "->")
+// CHECK-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires fix: "." to "->")
+// CHECK-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires fix: "." to "->")
+// CHECK-NOT: StructDecl:{TypedText X}{Text ::} (75) (requires fix: "." to "->")
+// CHECK-NOT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires fix: "." to "->")
+// CHECK: Completion contexts:
+// CHECK-NEXT: Dot member access
+
+// CHECK-WITH-CORRECTION: CXXMethod:{ResultType void}{TypedText doSomething}{LeftParen (}{RightParen )} (34) (requires fix: "." to "->")
+// CHECK-WITH-CORRECTION-NEXT: FieldDecl:{ResultType int}{TypedText field} (35) (requires fix: "." to "->")
+// CHECK-WITH-CORRECTION-NEXT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires fix: "." to "->")
+// CHECK-WITH-CORRECTION-NEXT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires fix: "." to "->")
+// CHECK-WITH-CORRECTION-NEXT: StructDecl:{TypedText X}{Text ::} (75) (requires fix: "." to "->")
+// CHECK-WITH-CORRECTION-NEXT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires fix: "." to "->")
+// CHECK-WITH-CORRECTION-NEXT: Completion contexts:
+// CHECK-WITH-CORRECTION-NEXT: Dot member access
+
+// CHECK-ARROW-TO-DOT-NOT: CXXMethod:{ResultType void}{TypedText doSomething}{LeftParen (}{RightParen )} (34) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-NOT: FieldDecl:{ResultType int}{TypedText field} (35) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-NOT: StructDecl:{TypedText X}{Text ::} (75) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-NOT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT: Completion contexts:
+// CHECK-ARROW-TO-DOT-NEXT: Unknown
+
+// CHECK-ARROW-TO-DOT-WITH-CORRECTION: CXXMethod:{ResultType void}{TypedText doSomething}{LeftParen (}{RightParen )} (34) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: FieldDecl:{ResultType int}{TypedText field} (35) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: StructDecl:{TypedText X}{Text ::} (75) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires fix: "->" to ".")
+// HECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: Completion contexts:
+// HECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: Arrow member access
Index: include/clang-c/Index.h
===================================================================
--- include/clang-c/Index.h
+++ include/clang-c/Index.h
@@ -32,7 +32,7 @@
* compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
*/
#define CINDEX_VERSION_MAJOR 0
-#define CINDEX_VERSION_MINOR 48
+#define CINDEX_VERSION_MINOR 49
#define CINDEX_VERSION_ENCODE(major, minor) ( \
((major) * 10000) \
@@ -5228,6 +5228,42 @@
} CXCodeCompleteResults;
/**
+ * \brief Retrieve the number of fix-its for the given completion string.
+ */
+CINDEX_LINKAGE unsigned
+clang_getCompletionNumFixIts(CXCodeCompleteResults *results,
+ unsigned completion_index);
+
+/**
+ * \brief FixIts that *must* be applied before inserting the text for the
+ * corresponding completion item.
+ *
+ * Completion items with non-empty fixits will not be returned by default, they
+ * should be explicitly requested by setting CXCodeComplete_IncludeFixIts. For
+ * the editors to be able to compute position of the cursor for the completion
+ * item itself, the following conditions are guaranteed to hold for RemoveRange
+ * of the stored fixits:
+ * - Ranges in the fixits are guaranteed to never contain the completion
+ * point (or identifier under completion point, if any) inside them, except
+ * at the start or at the end of the range.
+ * - If a fixit range starts or ends with completion point (or starts or
+ * ends after the identifier under completion point), it will contain at
+ * least one character. It allows to unambiguously recompute completion
+ * point after applying the fixit.
+ * The intuition is that provided fixits change code around the identifier we
+ * complete, but are not allowed to touch the identifier itself or the
+ * completion point. One example of completion items with corrections are the
+ * ones replacing '.' with '->' and vice versa:
+ * std::unique_ptr<std::vector<int>> vec_ptr;
+ * vec_ptr.^ // completion returns an item 'push_back', replacing '.'
+ * with '->' vec_ptr->^ // completion returns an item 'release',
+ * replacing '->' with '.'
+ */
+CINDEX_LINKAGE CXString clang_getCompletionFixIt(
+ CXCodeCompleteResults *results, unsigned completion_index,
+ unsigned fixit_index, CXSourceRange *replacement_range);
+
+/**
* \brief Flags that can be passed to \c clang_codeCompleteAt() to
* modify its behavior.
*
@@ -5258,7 +5294,13 @@
* defined in the preamble. There's no guarantee any particular entity is
* omitted. This may be useful if the headers are indexed externally.
*/
- CXCodeComplete_SkipPreamble = 0x08
+ CXCodeComplete_SkipPreamble = 0x08,
+
+ /**
+ * \brief Whether to include completion items with small
+ * fix-its, e.g. change '.' to '->' on member access, etc.
+ */
+ CXCodeComplete_IncludeFixIts = 0x10
};
/**
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits