Skip to content

Commit

Permalink
Optionally add code completion results for arrow instead of dot
Browse files Browse the repository at this point in the history
Currently getting such completions requires source correction, reparsing
and calling completion again. And if it shows no results and rollback is
required then it costs one more reparse.

With this change it's possible to get all results which can be later
filtered to split changes which require correction.

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

llvm-svn: 333272
  • Loading branch information
Ivan Donchevskii committed May 25, 2018
1 parent 0155bf0 commit b4670fc
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 93 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Driver/CC1Options.td
Expand Up @@ -445,6 +445,8 @@ def no_code_completion_ns_level_decls : Flag<["-"], "no-code-completion-ns-level
HelpText<"Do not include declarations inside namespaces (incl. global namespace) in the code-completion results.">;
def code_completion_brief_comments : Flag<["-"], "code-completion-brief-comments">,
HelpText<"Include brief documentation comments in code-completion results.">;
def code_completion_with_fixits : Flag<["-"], "code-completion-with-fixits">,
HelpText<"Include code completion results which require small fix-its.">;
def disable_free : Flag<["-"], "disable-free">,
HelpText<"Disable freeing of memory on exit">;
def discard_value_names : Flag<["-"], "discard-value-names">,
Expand Down
41 changes: 37 additions & 4 deletions clang/include/clang/Sema/CodeCompleteConsumer.h
Expand Up @@ -783,6 +783,33 @@ class CodeCompletionResult {
/// The availability of this result.
CXAvailabilityKind Availability = CXAvailability_Available;

/// 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
/// 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
/// 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;
/// In 'vec_ptr.^', one of completion items is 'push_back', it requires
/// replacing '.' with '->'.
/// In 'vec_ptr->^', one of completion items is 'release', it requires
/// replacing '->' with '.'.
std::vector<FixItHint> FixIts;

/// Whether this result is hidden by another name.
bool Hidden : 1;

Expand All @@ -807,15 +834,17 @@ class CodeCompletionResult {
NestedNameSpecifier *Qualifier = nullptr;

/// Build a result that refers to a declaration.
CodeCompletionResult(const NamedDecl *Declaration,
unsigned Priority,
CodeCompletionResult(const NamedDecl *Declaration, unsigned Priority,
NestedNameSpecifier *Qualifier = nullptr,
bool QualifierIsInformative = false,
bool Accessible = true)
bool Accessible = true,
std::vector<FixItHint> FixIts = std::vector<FixItHint>())
: Declaration(Declaration), Priority(Priority), Kind(RK_Declaration),
Hidden(false), QualifierIsInformative(QualifierIsInformative),
StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
DeclaringEntity(false), Qualifier(Qualifier) {
DeclaringEntity(false), Qualifier(Qualifier),
FixIts(std::move(FixIts)) {
//FIXME: Add assert to check FixIts range requirements.
computeCursorKindAndAvailability(Accessible);
}

Expand Down Expand Up @@ -1027,6 +1056,10 @@ class CodeCompleteConsumer {
return CodeCompleteOpts.IncludeBriefComments;
}

/// Whether to include completion items with small fix-its, e.g. change
/// '.' to '->' on member access, etc.
bool includeFixIts() const { return CodeCompleteOpts.IncludeFixIts; }

/// Hint whether to load data from the external AST in order to provide
/// full results. If false, declarations from the preamble may be omitted.
bool loadExternal() const {
Expand Down
6 changes: 5 additions & 1 deletion clang/include/clang/Sema/CodeCompleteOptions.h
Expand Up @@ -39,10 +39,14 @@ class CodeCompleteOptions {
/// If false, namespace-level declarations from the preamble may be omitted.
unsigned LoadExternal : 1;

/// Include results after corrections (small fix-its), e.g. change '.' to '->'
/// on member access, etc.
unsigned IncludeFixIts : 1;

CodeCompleteOptions()
: IncludeMacros(0), IncludeCodePatterns(0), IncludeGlobals(1),
IncludeNamespaceLevelDecls(1), IncludeBriefComments(0),
LoadExternal(1) {}
LoadExternal(1), IncludeFixIts(0) {}
};

} // namespace clang
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Sema/Sema.h
Expand Up @@ -10231,7 +10231,7 @@ class Sema {
struct CodeCompleteExpressionData;
void CodeCompleteExpression(Scope *S,
const CodeCompleteExpressionData &Data);
void CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
void CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base, Expr *OtherOpBase,
SourceLocation OpLoc, bool IsArrow,
bool IsBaseExprStatement);
void CodeCompletePostfixExpression(Scope *S, ExprResult LHS);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Frontend/ASTUnit.cpp
Expand Up @@ -2122,6 +2122,7 @@ void ASTUnit::CodeComplete(
CodeCompleteOpts.IncludeGlobals = CachedCompletionResults.empty();
CodeCompleteOpts.IncludeBriefComments = IncludeBriefComments;
CodeCompleteOpts.LoadExternal = Consumer.loadExternal();
CodeCompleteOpts.IncludeFixIts = Consumer.includeFixIts();

assert(IncludeBriefComments == this->IncludeBriefCommentsInCodeCompletion);

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Expand Up @@ -1536,6 +1536,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
= !Args.hasArg(OPT_no_code_completion_ns_level_decls);
Opts.CodeCompleteOpts.IncludeBriefComments
= Args.hasArg(OPT_code_completion_brief_comments);
Opts.CodeCompleteOpts.IncludeFixIts
= Args.hasArg(OPT_code_completion_with_fixits);

Opts.OverrideRecordLayoutsFile
= Args.getLastArgValue(OPT_foverride_record_layout_EQ);
Expand Down
26 changes: 21 additions & 5 deletions clang/lib/Parse/ParseExpr.cpp
Expand Up @@ -1703,8 +1703,10 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
CXXScopeSpec SS;
ParsedType ObjectType;
bool MayBePseudoDestructor = false;
Expr* OrigLHS = !LHS.isInvalid() ? LHS.get() : nullptr;

if (getLangOpts().CPlusPlus && !LHS.isInvalid()) {
Expr *Base = LHS.get();
Expr *Base = OrigLHS;
const Type* BaseType = Base->getType().getTypePtrOrNull();
if (BaseType && Tok.is(tok::l_paren) &&
(BaseType->isFunctionType() ||
Expand All @@ -1729,11 +1731,25 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
}

if (Tok.is(tok::code_completion)) {
tok::TokenKind CorrectedOpKind =
OpKind == tok::arrow ? tok::period : tok::arrow;
ExprResult CorrectedLHS(/*IsInvalid=*/true);
if (getLangOpts().CPlusPlus && OrigLHS) {
const bool DiagsAreSuppressed = Diags.getSuppressAllDiagnostics();
Diags.setSuppressAllDiagnostics(true);
CorrectedLHS = Actions.ActOnStartCXXMemberReference(
getCurScope(), OrigLHS, OpLoc, CorrectedOpKind, ObjectType,
MayBePseudoDestructor);
Diags.setSuppressAllDiagnostics(DiagsAreSuppressed);
}

Expr *Base = LHS.get();
Expr *CorrectedBase = CorrectedLHS.get();

// Code completion for a member access expression.
if (Expr *Base = LHS.get())
Actions.CodeCompleteMemberReferenceExpr(
getCurScope(), Base, OpLoc, OpKind == tok::arrow,
ExprStatementTokLoc == Base->getLocStart());
Actions.CodeCompleteMemberReferenceExpr(
getCurScope(), Base, CorrectedBase, OpLoc, OpKind == tok::arrow,
Base && ExprStatementTokLoc == Base->getLocStart());

cutOffParsing();
return ExprError();
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/Sema/CodeCompleteConsumer.cpp
Expand Up @@ -554,6 +554,24 @@ PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
if (const char *BriefComment = CCS->getBriefComment())
OS << " : " << BriefComment;
}
for (const FixItHint &FixIt : Results[I].FixIts) {
const SourceLocation BLoc = FixIt.RemoveRange.getBegin();
const SourceLocation ELoc = FixIt.RemoveRange.getEnd();

SourceManager &SM = SemaRef.SourceMgr;
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc);
std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc);
// Adjust for token ranges.
if (FixIt.RemoveRange.isTokenRange())
EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, SemaRef.LangOpts);

OS << " (requires fix-it:"
<< " {" << SM.getLineNumber(BInfo.first, BInfo.second) << ':'
<< SM.getColumnNumber(BInfo.first, BInfo.second) << '-'
<< SM.getLineNumber(EInfo.first, EInfo.second) << ':'
<< SM.getColumnNumber(EInfo.first, EInfo.second) << "}"
<< " to \"" << FixIt.CodeToInsert << "\")";
}
OS << '\n';
break;

Expand Down

0 comments on commit b4670fc

Please sign in to comment.