Skip to content

[WIP][CodeCompletion] Migrate argument position completion to the solver-based implementation #38266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion include/swift/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,12 @@ enum class FixKind : uint8_t {

/// Specify a type for an explicitly written placeholder that could not be
/// resolved.
SpecifyTypeForPlaceholder
SpecifyTypeForPlaceholder,

/// Ignore all failures in call or subscript arguments after the argument
/// containing the code completion location. This is only produced when
/// solving for code completion.
IgnoreFailureAfterCompletionArg,
};

class ConstraintFix {
Expand Down Expand Up @@ -1731,6 +1736,27 @@ class SkipUnhandledConstructInResultBuilder final : public ConstraintFix {
NominalTypeDecl *builder, ConstraintLocator *locator);
};

class IgnoreFailureAfterCompletionArg final : public ConstraintFix {
IgnoreFailureAfterCompletionArg(ConstraintSystem &cs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::IgnoreFailureAfterCompletionArg, locator,
true) {}

public:
std::string getName() const override {
return "ignore arguments after the code completion position in a call or "
"subscript";
}

bool diagnose(const Solution &solution, bool asNote = false) const override {
// Diagnostics are ignored when solving for code completion.
return false;
}

static IgnoreFailureAfterCompletionArg *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};

class AllowTupleSplatForSingleParameter final : public ConstraintFix {
using Param = AnyFunctionType::Param;

Expand Down
79 changes: 58 additions & 21 deletions include/swift/Sema/CodeCompletionTypeChecking.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,24 @@ namespace swift {
}

class TypeCheckCompletionCallback {
bool GotCallback = false;

public:
virtual ~TypeCheckCompletionCallback() {}

/// Called for each solution produced while type-checking an expression
/// that the code completion expression participates in.
virtual void sawSolution(const constraints::Solution &solution) = 0;
virtual ~TypeCheckCompletionCallback() {}
virtual void sawSolution(const constraints::Solution &solution) {
GotCallback = true;
};

/// True if at least one solution was passed via the \c sawSolution
/// callback.
bool gotCallback() const { return GotCallback; }

/// Typecheck the code completion expression in its outermost expression
/// context, calling \c sawSolution for each solution formed.
virtual void fallbackTypeCheck(DeclContext *DC);
};


Expand All @@ -58,28 +71,21 @@ namespace swift {
};

private:
DeclContext *DC;
CodeCompletionExpr *CompletionExpr;
SmallVector<Result, 4> Results;
llvm::DenseMap<std::pair<Type, Decl*>, size_t> BaseToSolutionIdx;
bool GotCallback = false;

public:
DotExprTypeCheckCompletionCallback(DeclContext *DC,
CodeCompletionExpr *CompletionExpr)
: DC(DC), CompletionExpr(CompletionExpr) {}
DotExprTypeCheckCompletionCallback(CodeCompletionExpr *CompletionExpr)
: CompletionExpr(CompletionExpr) {}

/// Get the results collected from any sawSolutions() callbacks recevied so
/// far.
ArrayRef<Result> getResults() const { return Results; }

/// True if at least one solution was passed via the \c sawSolution
/// callback.
bool gotCallback() const { return GotCallback; }

/// Typecheck the code completion expression in isolation, calling
/// \c sawSolution for each solution formed.
void fallbackTypeCheck();
void fallbackTypeCheck(DeclContext *DC) override;

void sawSolution(const constraints::Solution &solution) override;
};
Expand All @@ -97,22 +103,13 @@ namespace swift {
private:
CodeCompletionExpr *CompletionExpr;
SmallVector<Result, 4> Results;
bool GotCallback = false;

public:
UnresolvedMemberTypeCheckCompletionCallback(CodeCompletionExpr *CompletionExpr)
: CompletionExpr(CompletionExpr) {}

ArrayRef<Result> getResults() const { return Results; }

/// True if at least one solution was passed via the \c sawSolution
/// callback.
bool gotCallback() const { return GotCallback; }

/// Typecheck the code completion expression in its outermost expression
/// context, calling \c sawSolution for each solution formed.
void fallbackTypeCheck(DeclContext *DC);

void sawSolution(const constraints::Solution &solution) override;
};

Expand All @@ -139,6 +136,46 @@ namespace swift {

void sawSolution(const constraints::Solution &solution) override;
};

class ArgumentTypeCheckCompletionCallback
: public TypeCheckCompletionCallback {
public:
struct Result {
/// The type associated with the code completion expression itself.
Type ExpectedType;
/// True if this is a subscript rather than a function call.
bool IsSubscript;
/// The FuncDecl or SubscriptDecl associated with the call.
ValueDecl *FuncD;
/// The type of the function being called.
Type FuncTy;
/// The index of the argument containing the completion location
unsigned ArgIdx;
/// The index of the parameter corresponding to the completion argument.
Optional<unsigned> ParamIdx;
/// The indices of all params that were bound to non-synthesized
/// arguments.
SmallVector<unsigned, 16> ClaimedParamIndices;
/// True if the completion is a noninitial term in a variadic argument.
bool IsNoninitialVariadic;
/// The base type of the call/subscript (null for free functions).
Type BaseType;
/// True if an argument label precedes the completion location.
bool HasLabel;
};

private:
CodeCompletionExpr *CompletionExpr;
SmallVector<Result, 4> Results;

public:
ArgumentTypeCheckCompletionCallback(CodeCompletionExpr *CompletionExpr)
: CompletionExpr(CompletionExpr) {}

ArrayRef<Result> getResults() const { return Results; }

void sawSolution(const constraints::Solution &solution) override;
};
}

#endif
69 changes: 57 additions & 12 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ class SavedTypeVariableBinding {
/// The parent or fixed type.
llvm::PointerUnion<TypeVariableType *, TypeBase *> ParentOrFixed;

/// The locator associated with bound type
ConstraintLocator *BoundLocator;

public:
explicit SavedTypeVariableBinding(TypeVariableType *typeVar);

Expand Down Expand Up @@ -239,6 +242,8 @@ class TypeVariableType::Implementation {
/// type is bound.
llvm::PointerUnion<TypeVariableType *, TypeBase *> ParentOrFixed;

constraints::ConstraintLocator *BoundLocator = nullptr;

/// The corresponding node in the constraint graph.
constraints::ConstraintGraphNode *GraphNode = nullptr;

Expand Down Expand Up @@ -338,6 +343,12 @@ class TypeVariableType::Implementation {
return locator;
}

/// Retrieve the locator describing where the type bound to this type variable
/// originated.
constraints::ConstraintLocator *getBoundLocator() const {
return BoundLocator;
}

/// Retrieve the generic parameter opened by this type variable.
GenericTypeParamType *getGenericParameter() const;

Expand All @@ -360,6 +371,10 @@ class TypeVariableType::Implementation {
/// a type of a key path expression.
bool isKeyPathType() const;

/// Determine whether this type variable represents a code completion
/// expression.
bool isCodeCompletionToken() const;

/// Retrieve the representative of the equivalence class to which this
/// type variable belongs.
///
Expand Down Expand Up @@ -460,14 +475,15 @@ class TypeVariableType::Implementation {
}

/// Assign a fixed type to this equivalence class.
void assignFixedType(Type type,
void assignFixedType(Type type, constraints::ConstraintLocator *loc,
constraints::SavedTypeVariableBindings *record) {
assert((!getFixedType(0) || getFixedType(0)->isEqual(type)) &&
"Already has a fixed type!");
auto rep = getRepresentative(record);
if (record)
rep->getImpl().recordBinding(*record);
rep->getImpl().ParentOrFixed = type.getPointer();
rep->getImpl().BoundLocator = loc;
}

void setCanBindToLValue(constraints::SavedTypeVariableBindings *record,
Expand Down Expand Up @@ -3053,9 +3069,9 @@ class ConstraintSystem {
return TypeVariables.count(typeVar) > 0;
}

/// Whether the given expression's source range contains the code
/// Whether the given ASTNode's source range contains the code
/// completion location.
bool containsCodeCompletionLoc(Expr *expr) const;
bool containsCodeCompletionLoc(ASTNode node) const;

void setClosureType(const ClosureExpr *closure, FunctionType *type) {
assert(closure);
Expand Down Expand Up @@ -3904,7 +3920,7 @@ class ConstraintSystem {
/// \param notifyBindingInference Whether to notify binding inference about
/// the change to this type variable.
void assignFixedType(TypeVariableType *typeVar, Type type,
bool updateState = true,
ConstraintLocator *loc, bool updateState = true,
bool notifyBindingInference = true);

/// Determine if the type in question is an Array<T> and, if so, provide the
Expand Down Expand Up @@ -5321,8 +5337,38 @@ class MatchCallArgumentListener {
/// \returns true to indicate that this should cause a failure, false
/// otherwise.
virtual bool relabelArguments(ArrayRef<Identifier> newNames);

/// Indicates that arguments after the code completion token were not valid
/// and ignored.
///
/// \returns true to indicate that this should cause a failure, false
/// otherwise.
virtual bool invalidArgumentsAfterCompletion();
};

/// For a callsite containing a code completion expression, stores the index of
/// the arg containing it along with the index of the first trailing closure.
struct CompletionArgInfo {
unsigned completionIdx;
Optional<unsigned> firstTrailingIdx;
unsigned argCount;

/// \returns true if the given argument index is possibly about to be written
/// by the user (given the completion index) so shouldn't be penalised as
/// missing when ranking solutions.
bool allowsMissingArgAt(unsigned argInsertIdx, AnyFunctionType::Param param);

/// \returns true if the argument containing the completion location is before
/// the argument with the given index.
bool isBefore(unsigned argIdx) { return completionIdx < argIdx; }
};

/// Extracts the index of the argument containing the code completion location
/// from the provided anchor if it's a \c CallExpr, \c SubscriptExpr, or
/// \c ObjectLiteralExpr).
Optional<CompletionArgInfo>
getCompletionArgInfo(ASTNode anchor, constraints::ConstraintSystem &cs);

/// Match the call arguments (as described by the given argument type) to
/// the parameters (as described by the given parameter type).
///
Expand All @@ -5343,14 +5389,13 @@ class MatchCallArgumentListener {
/// \returns the bindings produced by performing this matching, or \c None if
/// the match failed.
Optional<MatchCallArgumentResult>
matchCallArguments(
SmallVectorImpl<AnyFunctionType::Param> &args,
ArrayRef<AnyFunctionType::Param> params,
const ParameterListInfo &paramInfo,
Optional<unsigned> unlabeledTrailingClosureIndex,
bool allowFixes,
MatchCallArgumentListener &listener,
Optional<TrailingClosureMatching> trailingClosureMatching);
matchCallArguments(SmallVectorImpl<AnyFunctionType::Param> &args,
ArrayRef<AnyFunctionType::Param> params,
const ParameterListInfo &paramInfo,
Optional<unsigned> unlabeledTrailingClosureIndex,
Optional<CompletionArgInfo> completionInfo, bool allowFixes,
MatchCallArgumentListener &listener,
Optional<TrailingClosureMatching> trailingClosureMatching);

ConstraintSystem::TypeMatchResult
matchCallArguments(ConstraintSystem &cs,
Expand Down
Loading