Skip to content

Commit

Permalink
[CodeComplete] Propagate preferred types through parser in more cases
Browse files Browse the repository at this point in the history
Preferred types are used by code completion for ranking. This commit
considerably increases the number of points in code where those types
are propagated.

In order to avoid complicating signatures of Parser's methods, a
preferred type is kept as a member variable in the parser and updated
during parsing.

Differential revision: https://reviews.llvm.org/D56723

llvm-svn: 352788
  • Loading branch information
ilya-biryukov committed Jan 31, 2019
1 parent 240a90a commit 4f9543b
Show file tree
Hide file tree
Showing 9 changed files with 393 additions and 136 deletions.
7 changes: 7 additions & 0 deletions clang/include/clang/Parse/Parser.h
Expand Up @@ -74,6 +74,10 @@ class Parser : public CodeCompletionHandler {
// a statement).
SourceLocation PrevTokLocation;

/// Tracks an expected type for the current token when parsing an expression.
/// Used by code completion for ranking.
PreferredTypeBuilder PreferredType;

unsigned short ParenCount = 0, BracketCount = 0, BraceCount = 0;
unsigned short MisplacedModuleBeginCount = 0;

Expand Down Expand Up @@ -840,13 +844,15 @@ class Parser : public CodeCompletionHandler {
///
class TentativeParsingAction {
Parser &P;
PreferredTypeBuilder PrevPreferredType;
Token PrevTok;
size_t PrevTentativelyDeclaredIdentifierCount;
unsigned short PrevParenCount, PrevBracketCount, PrevBraceCount;
bool isActive;

public:
explicit TentativeParsingAction(Parser& p) : P(p) {
PrevPreferredType = P.PreferredType;
PrevTok = P.Tok;
PrevTentativelyDeclaredIdentifierCount =
P.TentativelyDeclaredIdentifiers.size();
Expand All @@ -866,6 +872,7 @@ class Parser : public CodeCompletionHandler {
void Revert() {
assert(isActive && "Parsing action was finished!");
P.PP.Backtrack();
P.PreferredType = PrevPreferredType;
P.Tok = PrevTok;
P.TentativelyDeclaredIdentifiers.resize(
PrevTentativelyDeclaredIdentifierCount);
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Sema/CodeCompleteConsumer.h
Expand Up @@ -380,6 +380,7 @@ class CodeCompletionContext {
/// if the expression is a variable initializer or a function argument, the
/// type of the corresponding variable or function parameter.
QualType getPreferredType() const { return PreferredType; }
void setPreferredType(QualType T) { PreferredType = T; }

/// Retrieve the type of the base object in a member-access
/// expression.
Expand Down
46 changes: 41 additions & 5 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -273,6 +273,41 @@ class FileNullabilityMap {
}
};

/// Keeps track of expected type during expression parsing. The type is tied to
/// a particular token, all functions that update or consume the type take a
/// start location of the token they are looking at as a parameter. This allows
/// to avoid updating the type on hot paths in the parser.
class PreferredTypeBuilder {
public:
PreferredTypeBuilder() = default;
explicit PreferredTypeBuilder(QualType Type) : Type(Type) {}

void enterCondition(Sema &S, SourceLocation Tok);
void enterReturn(Sema &S, SourceLocation Tok);
void enterVariableInit(SourceLocation Tok, Decl *D);

void enterParenExpr(SourceLocation Tok, SourceLocation LParLoc);
void enterUnary(Sema &S, SourceLocation Tok, tok::TokenKind OpKind,
SourceLocation OpLoc);
void enterBinary(Sema &S, SourceLocation Tok, Expr *LHS, tok::TokenKind Op);
void enterMemAccess(Sema &S, SourceLocation Tok, Expr *Base);
void enterSubscript(Sema &S, SourceLocation Tok, Expr *LHS);
/// Handles all type casts, including C-style cast, C++ casts, etc.
void enterTypeCast(SourceLocation Tok, QualType CastType);

QualType get(SourceLocation Tok) const {
if (Tok == ExpectedLoc)
return Type;
return QualType();
}

private:
/// Start position of a token for which we store expected type.
SourceLocation ExpectedLoc;
/// Expected type for a token starting at ExpectedLoc.
QualType Type;
};

/// Sema - This implements semantic analysis and AST building for C.
class Sema {
Sema(const Sema &) = delete;
Expand Down Expand Up @@ -10371,11 +10406,14 @@ class Sema {
struct CodeCompleteExpressionData;
void CodeCompleteExpression(Scope *S,
const CodeCompleteExpressionData &Data);
void CodeCompleteExpression(Scope *S, QualType PreferredType);
void CodeCompleteExpression(Scope *S, QualType PreferredType,
bool IsParenthesized = false);
void CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base, Expr *OtherOpBase,
SourceLocation OpLoc, bool IsArrow,
bool IsBaseExprStatement);
void CodeCompletePostfixExpression(Scope *S, ExprResult LHS);
bool IsBaseExprStatement,
QualType PreferredType);
void CodeCompletePostfixExpression(Scope *S, ExprResult LHS,
QualType PreferredType);
void CodeCompleteTag(Scope *S, unsigned TagSpec);
void CodeCompleteTypeQualifiers(DeclSpec &DS);
void CodeCompleteFunctionQualifiers(DeclSpec &DS, Declarator &D,
Expand All @@ -10397,9 +10435,7 @@ class Sema {
IdentifierInfo *II,
SourceLocation OpenParLoc);
void CodeCompleteInitializer(Scope *S, Decl *D);
void CodeCompleteReturn(Scope *S);
void CodeCompleteAfterIf(Scope *S);
void CodeCompleteBinaryRHS(Scope *S, Expr *LHS, tok::TokenKind Op);

void CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS,
bool EnteringContext, QualType BaseType);
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Parse/ParseDecl.cpp
Expand Up @@ -2293,7 +2293,8 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
return nullptr;
}

ExprResult Init(ParseInitializer());
PreferredType.enterVariableInit(Tok.getLocation(), ThisDecl);
ExprResult Init = ParseInitializer();

// If this is the only decl in (possibly) range based for statement,
// our best guess is that the user meant ':' instead of '='.
Expand Down
53 changes: 36 additions & 17 deletions clang/lib/Parse/ParseExpr.cpp
Expand Up @@ -158,7 +158,8 @@ Parser::ParseExpressionWithLeadingExtension(SourceLocation ExtLoc) {
/// Parse an expr that doesn't include (top-level) commas.
ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) {
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Expression);
Actions.CodeCompleteExpression(getCurScope(),
PreferredType.get(Tok.getLocation()));
cutOffParsing();
return ExprError();
}
Expand Down Expand Up @@ -271,7 +272,10 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
getLangOpts().CPlusPlus11);
SourceLocation ColonLoc;

auto SavedType = PreferredType;
while (1) {
// Every iteration may rely on a preferred type for the whole expression.
PreferredType = SavedType;
// If this token has a lower precedence than we are allowed to parse (e.g.
// because we are called recursively, or because the token is not a binop),
// then we are done!
Expand Down Expand Up @@ -392,15 +396,8 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
}
}

// Code completion for the right-hand side of a binary expression goes
// through a special hook that takes the left-hand side into account.
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteBinaryRHS(getCurScope(), LHS.get(),
OpToken.getKind());
cutOffParsing();
return ExprError();
}

PreferredType.enterBinary(Actions, Tok.getLocation(), LHS.get(),
OpToken.getKind());
// Parse another leaf here for the RHS of the operator.
// ParseCastExpression works here because all RHS expressions in C have it
// as a prefix, at least. However, in C++, an assignment-expression could
Expand Down Expand Up @@ -763,6 +760,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
bool isVectorLiteral) {
ExprResult Res;
tok::TokenKind SavedKind = Tok.getKind();
auto SavedType = PreferredType;
NotCastExpr = false;

// This handles all of cast-expression, unary-expression, postfix-expression,
Expand Down Expand Up @@ -1114,6 +1112,9 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
// -- cast-expression
Token SavedTok = Tok;
ConsumeToken();

PreferredType.enterUnary(Actions, Tok.getLocation(), SavedTok.getKind(),
SavedTok.getLocation());
// One special case is implicitly handled here: if the preceding tokens are
// an ambiguous cast expression, such as "(T())++", then we recurse to
// determine whether the '++' is prefix or postfix.
Expand All @@ -1135,6 +1136,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
case tok::amp: { // unary-expression: '&' cast-expression
// Special treatment because of member pointers
SourceLocation SavedLoc = ConsumeToken();
PreferredType.enterUnary(Actions, Tok.getLocation(), tok::amp, SavedLoc);
Res = ParseCastExpression(false, true);
if (!Res.isInvalid())
Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get());
Expand All @@ -1149,6 +1151,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
case tok::kw___real: // unary-expression: '__real' cast-expression [GNU]
case tok::kw___imag: { // unary-expression: '__imag' cast-expression [GNU]
SourceLocation SavedLoc = ConsumeToken();
PreferredType.enterUnary(Actions, Tok.getLocation(), SavedKind, SavedLoc);
Res = ParseCastExpression(false);
if (!Res.isInvalid())
Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get());
Expand Down Expand Up @@ -1423,7 +1426,8 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
Res = ParseBlockLiteralExpression();
break;
case tok::code_completion: {
Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Expression);
Actions.CodeCompleteExpression(getCurScope(),
PreferredType.get(Tok.getLocation()));
cutOffParsing();
return ExprError();
}
Expand Down Expand Up @@ -1458,6 +1462,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
// that the address of the function is being taken, which is illegal in CL.

// These can be followed by postfix-expr pieces.
PreferredType = SavedType;
Res = ParsePostfixExpressionSuffix(Res);
if (getLangOpts().OpenCL)
if (Expr *PostfixExpr = Res.get()) {
Expand Down Expand Up @@ -1497,13 +1502,17 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
// Now that the primary-expression piece of the postfix-expression has been
// parsed, see if there are any postfix-expression pieces here.
SourceLocation Loc;
auto SavedType = PreferredType;
while (1) {
// Each iteration relies on preferred type for the whole expression.
PreferredType = SavedType;
switch (Tok.getKind()) {
case tok::code_completion:
if (InMessageExpression)
return LHS;

Actions.CodeCompletePostfixExpression(getCurScope(), LHS);
Actions.CodeCompletePostfixExpression(
getCurScope(), LHS, PreferredType.get(Tok.getLocation()));
cutOffParsing();
return ExprError();

Expand Down Expand Up @@ -1545,6 +1554,7 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
Loc = T.getOpenLocation();
ExprResult Idx, Length;
SourceLocation ColonLoc;
PreferredType.enterSubscript(Actions, Tok.getLocation(), LHS.get());
if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
Idx = ParseBraceInitializer();
Expand Down Expand Up @@ -1726,6 +1736,8 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
bool MayBePseudoDestructor = false;
Expr* OrigLHS = !LHS.isInvalid() ? LHS.get() : nullptr;

PreferredType.enterMemAccess(Actions, Tok.getLocation(), OrigLHS);

if (getLangOpts().CPlusPlus && !LHS.isInvalid()) {
Expr *Base = OrigLHS;
const Type* BaseType = Base->getType().getTypePtrOrNull();
Expand Down Expand Up @@ -1772,7 +1784,8 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
// Code completion for a member access expression.
Actions.CodeCompleteMemberReferenceExpr(
getCurScope(), Base, CorrectedBase, OpLoc, OpKind == tok::arrow,
Base && ExprStatementTokLoc == Base->getBeginLoc());
Base && ExprStatementTokLoc == Base->getBeginLoc(),
PreferredType.get(Tok.getLocation()));

cutOffParsing();
return ExprError();
Expand Down Expand Up @@ -2326,14 +2339,16 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,
return ExprError();
SourceLocation OpenLoc = T.getOpenLocation();

PreferredType.enterParenExpr(Tok.getLocation(), OpenLoc);

ExprResult Result(true);
bool isAmbiguousTypeId;
CastTy = nullptr;

if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteOrdinaryName(getCurScope(),
ExprType >= CompoundLiteral? Sema::PCC_ParenthesizedExpression
: Sema::PCC_Expression);
Actions.CodeCompleteExpression(
getCurScope(), PreferredType.get(Tok.getLocation()),
/*IsParenthesized=*/ExprType >= CompoundLiteral);
cutOffParsing();
return ExprError();
}
Expand Down Expand Up @@ -2414,6 +2429,8 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,
T.consumeClose();
ColonProtection.restore();
RParenLoc = T.getCloseLocation();

PreferredType.enterTypeCast(Tok.getLocation(), Ty.get().get());
ExprResult SubExpr = ParseCastExpression(/*isUnaryExpression=*/false);

if (Ty.isInvalid() || SubExpr.isInvalid())
Expand Down Expand Up @@ -2544,6 +2561,7 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,
return ExprError();
}

PreferredType.enterTypeCast(Tok.getLocation(), CastTy.get());
// Parse the cast-expression that follows it next.
// TODO: For cast expression with CastTy.
Result = ParseCastExpression(/*isUnaryExpression=*/false,
Expand Down Expand Up @@ -2845,7 +2863,8 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
if (Completer)
Completer();
else
Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Expression);
Actions.CodeCompleteExpression(getCurScope(),
PreferredType.get(Tok.getLocation()));
cutOffParsing();
return true;
}
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Parse/ParseExprCXX.cpp
Expand Up @@ -1672,6 +1672,8 @@ Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) {
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();

PreferredType.enterTypeCast(Tok.getLocation(), TypeRep.get());

ExprVector Exprs;
CommaLocsTy CommaLocs;

Expand Down Expand Up @@ -1739,6 +1741,7 @@ Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt,
Sema::ConditionKind CK,
ForRangeInfo *FRI) {
ParenBraceBracketBalancer BalancerRAIIObj(*this);
PreferredType.enterCondition(Actions, Tok.getLocation());

if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Condition);
Expand Down Expand Up @@ -1858,6 +1861,7 @@ Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt,
diag::warn_cxx98_compat_generalized_initializer_lists);
InitExpr = ParseBraceInitializer();
} else if (CopyInitialization) {
PreferredType.enterVariableInit(Tok.getLocation(), DeclOut);
InitExpr = ParseAssignmentExpression();
} else if (Tok.is(tok::l_paren)) {
// This was probably an attempt to initialize the variable.
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Parse/ParseStmt.cpp
Expand Up @@ -1970,9 +1970,12 @@ StmtResult Parser::ParseReturnStatement() {

ExprResult R;
if (Tok.isNot(tok::semi)) {
if (!IsCoreturn)
PreferredType.enterReturn(Actions, Tok.getLocation());
// FIXME: Code completion for co_return.
if (Tok.is(tok::code_completion) && !IsCoreturn) {
Actions.CodeCompleteReturn(getCurScope());
Actions.CodeCompleteExpression(getCurScope(),
PreferredType.get(Tok.getLocation()));
cutOffParsing();
return StmtError();
}
Expand Down

0 comments on commit 4f9543b

Please sign in to comment.