[libclang] Optionally add code completion results for arrow instead of dot

Follow up for D41537 - libclang part.

Differential Revision: https://reviews.llvm.org/D46862

llvm-svn: 334593
This commit is contained in:
Ivan Donchevskii 2018-06-13 12:37:08 +00:00
parent b983ac6fe1
commit 3957e48a68
7 changed files with 310 additions and 41 deletions

View File

@ -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) \
@ -1327,7 +1327,7 @@ enum CXTranslationUnit_Flags {
CXTranslationUnit_SingleFileParse = 0x400,
/**
* \brief Used in combination with CXTranslationUnit_SkipFunctionBodies to
* Used in combination with CXTranslationUnit_SkipFunctionBodies to
* constrain the skipping of function bodies to the preamble.
*
* The function bodies of the main file are not skipped.
@ -4748,6 +4748,20 @@ typedef struct {
void *ptr_data;
} CXToken;
/**
* Get the raw lexical token starting with the given location.
*
* \param TU the translation unit whose text is being tokenized.
*
* \param Location the source location with which the token starts.
*
* \returns The token starting with the given location or NULL if no such token
* exist. The returned pointer must be freed with clang_disposeTokens before the
* translation unit is destroyed.
*/
CINDEX_LINKAGE CXToken *clang_getToken(CXTranslationUnit TU,
CXSourceLocation Location);
/**
* Determine the kind of the given token.
*/
@ -5243,6 +5257,70 @@ typedef struct {
unsigned NumResults;
} CXCodeCompleteResults;
/**
* Retrieve the number of fix-its for the given completion index.
*
* Calling this makes sense only if CXCodeComplete_IncludeCompletionsWithFixIts
* option was set.
*
* \param results The structure keeping all completion results
*
* \param completion_index The index of the completion
*
* \return The number of fix-its which must be applied before the completion at
* completion_index can be applied
*/
CINDEX_LINKAGE unsigned
clang_getCompletionNumFixIts(CXCodeCompleteResults *results,
unsigned completion_index);
/**
* Fix-its that *must* be applied before inserting the text for the
* corresponding completion.
*
* By default, clang_codeCompleteAt() only returns completions with empty
* fix-its. Extra completions with non-empty fix-its should be explicitly
* requested by setting CXCodeComplete_IncludeCompletionsWithFixIts.
*
* For the clients to be able to compute position of the cursor after applying
* fix-its, the following conditions are guaranteed to hold for
* replacement_range of the stored fix-its:
* - Ranges in the fix-its 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 fix-it 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 fix-it.
*
* The intuition is that provided fix-its change code around the identifier we
* complete, but are not allowed to touch the identifier itself or the
* completion point. One example of completions with corrections are the ones
* replacing '.' with '->' and vice versa:
*
* std::unique_ptr<std::vector<int>> vec_ptr;
* In 'vec_ptr.^', one of the completions is 'push_back', it requires
* replacing '.' with '->'.
* In 'vec_ptr->^', one of the completions is 'release', it requires
* replacing '->' with '.'.
*
* \param results The structure keeping all completion results
*
* \param completion_index The index of the completion
*
* \param fixit_index The index of the fix-it for the completion at
* completion_index
*
* \param replacement_range The fix-it range that must be replaced before the
* completion at completion_index can be applied
*
* \returns The fix-it string that must replace the code at replacement_range
* before the completion at completion_index can be applied
*/
CINDEX_LINKAGE CXString clang_getCompletionFixIt(
CXCodeCompleteResults *results, unsigned completion_index,
unsigned fixit_index, CXSourceRange *replacement_range);
/**
* Flags that can be passed to \c clang_codeCompleteAt() to
* modify its behavior.
@ -5274,7 +5352,13 @@ enum CXCodeComplete_Flags {
* 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,
/**
* Whether to include completions with small
* fix-its, e.g. change '.' to '->' on member access, etc.
*/
CXCodeComplete_IncludeCompletionsWithFixIts = 0x10
};
/**

View File

@ -783,30 +783,33 @@ public:
/// The availability of this result.
CXAvailabilityKind Availability = CXAvailability_Available;
/// FixIts that *must* be applied before inserting the text for the
/// corresponding completion item.
/// Fix-its that *must* be applied before inserting the text for the
/// corresponding completion.
///
/// Completion items with non-empty fixits will not be returned by default,
/// they should be explicitly requested by setting
/// CompletionOptions::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
/// By default, CodeCompletionBuilder only returns completions with empty
/// fix-its. Extra completions with non-empty fix-its should be explicitly
/// requested by setting CompletionOptions::IncludeFixIts.
///
/// For the clients to be able to compute position of the cursor after
/// applying fix-its, the following conditions are guaranteed to hold for
/// RemoveRange of the stored fix-its:
/// - Ranges in the fix-its 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
/// - If a fix-it 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:
/// point after applying the fix-it.
///
/// The intuition is that provided fix-its change code around the identifier
/// we complete, but are not allowed to touch the identifier itself or the
/// completion point. One example of completions with corrections are the ones
/// replacing '.' with '->' and vice versa:
///
/// std::unique_ptr<std::vector<int>> vec_ptr;
/// In 'vec_ptr.^', one of completion items is 'push_back', it requires
/// In 'vec_ptr.^', one of the completions is 'push_back', it requires
/// replacing '.' with '->'.
/// In 'vec_ptr->^', one of completion items is 'release', it requires
/// In 'vec_ptr->^', one of the completions is 'release', it requires
/// replacing '->' with '.'.
std::vector<FixItHint> FixIts;

View File

@ -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_FIXITS=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_FIXITS=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-it:{{.*}}
// CHECK-NOT: FieldDecl:{ResultType int}{TypedText field} (35) (requires fix-it:{{.*}}
// CHECK-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires fix-it:{{.*}}
// CHECK-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires fix-it:{{.*}}
// CHECK-NOT: StructDecl:{TypedText X}{Text ::} (75) (requires fix-it:{{.*}}
// CHECK-NOT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires fix-it:{{.*}}
// CHECK: Completion contexts:
// CHECK-NEXT: Dot member access
// CHECK-WITH-CORRECTION: CXXMethod:{ResultType void}{TypedText doSomething}{LeftParen (}{RightParen )} (34) (requires fix-it:{{.*}}
// CHECK-WITH-CORRECTION-NEXT: FieldDecl:{ResultType int}{TypedText field} (35) (requires fix-it:{{.*}}
// CHECK-WITH-CORRECTION-NEXT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires fix-it:{{.*}}
// CHECK-WITH-CORRECTION-NEXT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires fix-it:{{.*}}
// CHECK-WITH-CORRECTION-NEXT: StructDecl:{TypedText X}{Text ::} (75) (requires fix-it:{{.*}}
// CHECK-WITH-CORRECTION-NEXT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires fix-it:{{.*}}
// 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-it:{{.*}}
// CHECK-ARROW-TO-DOT-NOT: FieldDecl:{ResultType int}{TypedText field} (35) (requires fix-it:{{.*}}
// CHECK-ARROW-TO-DOT-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires fix-it:{{.*}}
// CHECK-ARROW-TO-DOT-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires fix-it:{{.*}}
// CHECK-ARROW-TO-DOT-NOT: StructDecl:{TypedText X}{Text ::} (75) (requires fix-it:{{.*}}
// CHECK-ARROW-TO-DOT-NOT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires fix-it:{{.*}}
// 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-it:{{.*}}
// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: FieldDecl:{ResultType int}{TypedText field} (35) (requires fix-it:{{.*}}
// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires fix-it:{{.*}}
// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires fix-it:{{.*}}
// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: StructDecl:{TypedText X}{Text ::} (75) (requires fix-it:{{.*}}
// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires fix-it:{{.*}}
// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: Completion contexts:
// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: Arrow member access

View File

@ -2270,8 +2270,33 @@ static void print_completion_string(CXCompletionString completion_string,
}
static void print_completion_result(CXCompletionResult *completion_result,
static void print_line_column(CXSourceLocation location, FILE *file) {
unsigned line, column;
clang_getExpansionLocation(location, NULL, &line, &column, NULL);
fprintf(file, "%d:%d", line, column);
}
static void print_token_range(CXTranslationUnit translation_unit,
CXSourceLocation start, FILE *file) {
CXToken *token = clang_getToken(translation_unit, start);
fprintf(file, "{");
if (token != NULL) {
CXSourceRange token_range = clang_getTokenExtent(translation_unit, *token);
print_line_column(clang_getRangeStart(token_range), file);
fprintf(file, "-");
print_line_column(clang_getRangeEnd(token_range), file);
clang_disposeTokens(translation_unit, token, 1);
}
fprintf(file, "}");
}
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;
@ -2339,7 +2364,20 @@ static void print_completion_result(CXCompletionResult *completion_result,
fprintf(file, "(brief comment: %s)", BriefCommentCString);
}
clang_disposeString(BriefComment);
unsigned i;
for (i = 0; i < clang_getCompletionNumFixIts(completion_results, index);
++i) {
CXSourceRange correction_range;
CXString FixIt = clang_getCompletionFixIt(completion_results, index, i,
&correction_range);
fprintf(file, " (requires fix-it: ");
print_token_range(translation_unit, clang_getRangeStart(correction_range),
file);
fprintf(file, " to \"%s\")", clang_getCString(FixIt));
clang_disposeString(FixIt);
}
fprintf(file, "\n");
}
@ -2438,6 +2476,8 @@ int perform_code_completion(int argc, const char **argv, int timing_only) {
completionOptions |= CXCodeComplete_IncludeBriefComments;
if (getenv("CINDEXTEST_COMPLETION_SKIP_PREAMBLE"))
completionOptions |= CXCodeComplete_SkipPreamble;
if (getenv("CINDEXTEST_COMPLETION_INCLUDE_FIXITS"))
completionOptions |= CXCodeComplete_IncludeCompletionsWithFixIts;
if (timing_only)
input += strlen("-code-completion-timing=");
@ -2502,7 +2542,7 @@ int perform_code_completion(int argc, const char **argv, int timing_only) {
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) {
@ -4402,10 +4442,10 @@ static void printLocation(CXSourceLocation L) {
CXFile File;
CXString FileName;
unsigned line, column, offset;
clang_getExpansionLocation(L, &File, &line, &column, &offset);
FileName = clang_getFileName(File);
fprintf(stderr, "%s:%d:%d", clang_getCString(FileName), line, column);
clang_disposeString(FileName);
}

View File

@ -6652,6 +6652,42 @@ static void getTokens(ASTUnit *CXXUnit, SourceRange Range,
} while (Lex.getBufferLocation() < EffectiveBufferEnd);
}
CXToken *clang_getToken(CXTranslationUnit TU, CXSourceLocation Location) {
LOG_FUNC_SECTION {
*Log << TU << ' ' << Location;
}
if (isNotUsableTU(TU)) {
LOG_BAD_TU(TU);
return NULL;
}
ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
if (!CXXUnit)
return NULL;
SourceLocation Begin = cxloc::translateSourceLocation(Location);
if (Begin.isInvalid())
return NULL;
SourceManager &SM = CXXUnit->getSourceManager();
std::pair<FileID, unsigned> DecomposedEnd = SM.getDecomposedLoc(Begin);
DecomposedEnd.second += Lexer::MeasureTokenLength(Begin, SM, CXXUnit->getLangOpts());
SourceLocation End = SM.getComposedLoc(DecomposedEnd.first, DecomposedEnd.second);
SmallVector<CXToken, 32> CXTokens;
getTokens(CXXUnit, SourceRange(Begin, End), CXTokens);
if (CXTokens.empty())
return NULL;
CXTokens.resize(1);
CXToken *Token = static_cast<CXToken *>(llvm::safe_malloc(sizeof(CXToken)));
memmove(Token, CXTokens.data(), sizeof(CXToken));
return Token;
}
void clang_tokenize(CXTranslationUnit TU, CXSourceRange Range,
CXToken **Tokens, unsigned *NumTokens) {
LOG_FUNC_SECTION {

View File

@ -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 @@ struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults {
/// A string containing the Objective-C selector entered thus far for a
/// message send.
std::string Selector;
/// 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());
}
/// Tracks the number of code-completion result objects that are
/// currently active.
///
@ -531,8 +575,10 @@ namespace {
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());
@ -541,8 +587,10 @@ namespace {
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 @@ clang_codeCompleteAt_Impl(CXTranslationUnit TU, const char *complete_filename,
unsigned options) {
bool IncludeBriefComments = options & CXCodeComplete_IncludeBriefComments;
bool SkipPreamble = options & CXCodeComplete_SkipPreamble;
bool IncludeFixIts = options & CXCodeComplete_IncludeCompletionsWithFixIts;
#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 @@ clang_codeCompleteAt_Impl(CXTranslationUnit TU, const char *complete_filename,
CodeCompleteOptions Opts;
Opts.IncludeBriefComments = IncludeBriefComments;
Opts.LoadExternal = !SkipPreamble;
Opts.IncludeFixIts = IncludeFixIts;
CaptureCompletionResults Capture(Opts, *Results, &TU);
// Perform completion.
@ -964,7 +1013,7 @@ namespace {
= (CodeCompletionString *)XR.CompletionString;
CodeCompletionString *Y
= (CodeCompletionString *)YR.CompletionString;
SmallString<256> XBuffer;
StringRef XText = GetTypedName(X, XBuffer);
SmallString<256> YBuffer;

View File

@ -168,12 +168,14 @@ clang_getClangVersion
clang_getCompletionAnnotation
clang_getCompletionAvailability
clang_getCompletionBriefComment
clang_getCompletionChunkCompletionString
clang_getCompletionChunkKind
clang_getCompletionChunkText
clang_getCompletionNumAnnotations
clang_getCompletionParent
clang_getCompletionPriority
clang_getCompletionChunkCompletionString
clang_getCompletionChunkKind
clang_getCompletionChunkText
clang_getCompletionNumFixIts
clang_getCompletionFixIt
clang_getCompletionNumAnnotations
clang_getCompletionParent
clang_getCompletionPriority
clang_getCursor
clang_getCursorAvailability
clang_getCursorCompletionString
@ -257,12 +259,13 @@ clang_getRemappingsFromFileList
clang_getResultType
clang_getSkippedRanges
clang_getSpecializedCursorTemplate
clang_getSpellingLocation
clang_getTUResourceUsageName
clang_getTemplateCursorKind
clang_getTokenExtent
clang_getTokenKind
clang_getTokenLocation
clang_getSpellingLocation
clang_getTUResourceUsageName
clang_getTemplateCursorKind
clang_getToken
clang_getTokenExtent
clang_getTokenKind
clang_getTokenLocation
clang_getTokenSpelling
clang_getTranslationUnitCursor
clang_getTranslationUnitSpelling