Skip to content

Migrate the remaining completion kinds to solver-based + rework type-checking of string interpolations #63717

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

Merged
merged 32 commits into from
Jul 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2ba70df
[CSClosure] Add `TapExpr` as a member of `SyntacticElementContext`
xedin Oct 10, 2022
e337a39
[ConstraintSystem] Implement tap expression checking in the solver
xedin Oct 26, 2022
096f6f8
[CSApply] Don't delay tap expression solution application
xedin Oct 26, 2022
60fa909
[CSSyntacticElement] Make sure that body of tap is solved in isolation
xedin Dec 24, 2022
773513a
[Sema] Run structural/availability diagnostics on tap bodies
xedin Dec 24, 2022
4b84391
[Tests] Adjust a couple of string interpolation test-cases
xedin Feb 15, 2023
d55f737
[ConstraintSystem] NFC: Replace LLVM_NODISCARD with [[nodiscard]] on …
xedin Mar 10, 2023
ac6ea62
[CSGen] NFC: Extract var reference collector to be used in more places
xedin Mar 15, 2023
2fd5b5f
[CSGen] Interpolations: Collect all vars referenced in the body to pr…
xedin Mar 15, 2023
4b091be
[Tests/Sema] NFC: Add a couple string interpolation test-cases
xedin Mar 15, 2023
ff0c942
[CSSyntacticElement] Fix solution applicator to identify context corr…
xedin Mar 27, 2023
77bd8ad
[CSGen/SyntaticElement] Remove logic that connects tap expressions to…
xedin Mar 27, 2023
6cec68e
[IDE] Ignore score kinds that represent implicit conversions when sol…
ahoppen Feb 23, 2023
4c186c0
[CodeCompletion] Fix code completion issues related to type checking …
ahoppen Feb 14, 2023
48dd99b
[IDE] Don't rank based on overload choices of function calls that con…
ahoppen Mar 14, 2023
0c41310
[CodeComplete] Skip conjunction elements unrelated to the code comple…
ahoppen Mar 14, 2023
1e723dd
[CodeCompletion] Don't increase the score for holes at the code compl…
ahoppen Feb 23, 2023
c385fe5
[CodeCompletion] Migrate PostfixExprParen to solver-based
ahoppen Feb 16, 2023
00eaed3
[CodeCompletion] Migrate postfix expr completion to solver-based
ahoppen Mar 14, 2023
4d1e44f
[CodeCompletion] Don't typecheck expression if result builder transfo…
ahoppen Feb 20, 2023
928a03a
[CodeCompletion] Migrate conforming methods list to solver-based
ahoppen Jun 30, 2022
d3270c1
[CodeCompletion] Calling a static function on a type is not unapplied
ahoppen Jun 30, 2022
c7e0bfa
[IDE] Adjust test cases for migrating all completion kinds to solver-…
ahoppen Feb 15, 2023
7193766
[IDE] Don’t return any result if the base expression’s type in postfi…
ahoppen Feb 17, 2023
1bacfe9
[IDE] Ignore unspported constructs in result builders in code complet…
ahoppen Mar 14, 2023
f2017b8
[IDE] Don't fallback type check if the completion expression is insid…
ahoppen Mar 14, 2023
c00428a
[IDE] New test cases that were failing at some point during solver-ba…
ahoppen Feb 20, 2023
527a79e
[IDE] Set constraint system options from `solveForCodeCompletion` in …
ahoppen Mar 11, 2023
4f5743c
[IDE] Adjust more test cases
ahoppen Mar 11, 2023
c453f2d
[Tests] NFC: Adjust new code completion tests to account for `any`
xedin Jun 21, 2023
ed4dc1b
Change `Optional` -> `llvm::Optional`
ahoppen Jul 7, 2023
597c0dc
[SourceKit] Fix crash when completing a parameter of an unapplied ins…
ahoppen Jul 7, 2023
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
11 changes: 9 additions & 2 deletions include/swift/IDE/CompletionLookup.h
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {

void getPostfixKeywordCompletions(Type ExprType, Expr *ParsedExpr);

void getValueExprCompletions(Type ExprType, ValueDecl *VD = nullptr);
/// Add code completion results after an expression of type \p ExprType.
/// This includes members as well as call patterns if \p ExprType is a
/// function type.
/// If \p IsDeclUnapplied is \c true, we are completing after a refernce to
/// \p VD that hasn't been called yet. Thus, \p VD has type \p ExprType and we
/// can use \p VD to enrich call pattern completions of \p ExprType.
void getValueExprCompletions(Type ExprType, ValueDecl *VD = nullptr,
bool IsDeclUnapplied = false);

void collectOperators(SmallVectorImpl<OperatorDecl *> &results);

Expand All @@ -529,7 +536,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {

void tryPostfixOperator(Expr *expr, PostfixOperatorDecl *op);

void addAssignmentOperator(Type RHSType, Type resultType);
void addAssignmentOperator(Type RHSType);

void addInfixOperatorCompletion(OperatorDecl *op, Type resultType,
Type RHSType);
Expand Down
60 changes: 53 additions & 7 deletions include/swift/IDE/PostfixCompletion.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,41 @@
namespace swift {
namespace ide {

/// Used to collect and store information needed to perform member completion
/// (\c CompletionKind::DotExpr ) from the solutions formed during expression
/// type-checking.
/// Used to collect and store information needed to perform postfix completion
/// (either <base>.#^COMPLETE^# or <base> #^COMPLETE^#).
class PostfixCompletionCallback : public TypeCheckCompletionCallback {
struct Result {
/// The type that we are completing on. Will never be null.
Type BaseTy;

/// The decl that we are completing on. Is \c nullptr if we are completing
/// on an expression.
ValueDecl *BaseDecl;

/// Whether \c BaseDecl refers to a function that has not been called yet.
/// In such cases, we know that \p BaseTy is the type of \p BaseDecl and we
/// can use \p BaseDecl for more detailed call pattern completions.
bool IsBaseDeclUnapplied;

/// If the expression we are completing on statically refers to a metatype,
/// that is if it's something like 'MyType'. In such cases we want to offer
/// constructor call pattern completions and don't want to suggeste
/// operators that work on metatypes.
bool BaseIsStaticMetaType;

/// The types that the completion is expected to produce.
SmallVector<Type, 4> ExpectedTypes;

/// Whether results that produce 'Void' should be disfavored. This happens
/// if the context is requiring a value. Once a completion produces 'Void',
/// we know that we can't retrieve a value from it anymore.
bool ExpectsNonVoid;
bool BaseIsStaticMetaType;

/// If the code completion expression occurs as a single statement in a
/// single-expression closure. In such cases we don't want to disfavor
/// results that produce 'Void' because the user might intend to make the
/// closure a multi-statment closure, in which case this expression is no
/// longer implicitly returned.
bool IsImplicitSingleExpressionReturn;

/// Whether the surrounding context is async and thus calling async
Expand All @@ -40,13 +65,24 @@ class PostfixCompletionCallback : public TypeCheckCompletionCallback {
/// haven't been saved to the AST.
llvm::DenseMap<AbstractClosureExpr *, ClosureActorIsolation>
ClosureActorIsolations;

/// Checks whether this result has the same \c BaseTy and \c BaseDecl as
/// \p Other and if the two can thus be merged to be one value lookup in
/// \c deliverResults.
bool canBeMergedWith(const Result &Other, DeclContext &DC) const;

/// Merge this result with \p Other. Assumes that they can be merged.
void merge(const Result &Other, DeclContext &DC);
};

CodeCompletionExpr *CompletionExpr;
DeclContext *DC;

SmallVector<Result, 4> Results;
llvm::DenseMap<std::pair<Type, Decl *>, size_t> BaseToSolutionIdx;

/// Add a result to \c Results, merging it with an existing result, if
/// possible.
void addResult(const Result &Res);

void sawSolutionImpl(const constraints::Solution &solution) override;

Expand All @@ -58,8 +94,18 @@ class PostfixCompletionCallback : public TypeCheckCompletionCallback {
/// \c sawSolution for each solution formed.
void fallbackTypeCheck(DeclContext *DC) override;

void deliverResults(Expr *BaseExpr, DeclContext *DC, SourceLoc DotLoc,
bool IsInSelector, CodeCompletionContext &CompletionCtx,
/// Deliver code completion results that were discoverd by \c sawSolution to
/// \p Consumer.
/// \param DotLoc If we are completing after a dot, the location of the dot,
/// otherwise an invalid SourceLoc.
/// \param IsInSelector Whether we are completing in an Objective-C selector.
/// \param IncludeOperators If operators should be suggested. Assumes that
/// \p DotLoc is invalid
/// \param HasLeadingSpace Whether there is a space separating the exiting
/// expression and the code completion token.
void deliverResults(SourceLoc DotLoc, bool IsInSelector,
bool IncludeOperators, bool HasLeadingSpace,
CodeCompletionContext &CompletionCtx,
CodeCompletionConsumer &Consumer);
};

Expand Down
12 changes: 12 additions & 0 deletions include/swift/IDE/UnresolvedMemberCompletion.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ class UnresolvedMemberTypeCheckCompletionCallback
/// Whether the surrounding context is async and thus calling async
/// functions is supported.
bool IsInAsyncContext;

/// Checks whether this result has the same \c BaseTy and \c BaseDecl as
/// \p Other and if the two can thus be merged to be one value lookup in
/// \c deliverResults.
bool canBeMergedWith(const Result &Other, DeclContext &DC) const;

/// Merge this result with \p Other. Assumes that they can be merged.
void merge(const Result &Other, DeclContext &DC);
};

CodeCompletionExpr *CompletionExpr;
Expand All @@ -40,6 +48,10 @@ class UnresolvedMemberTypeCheckCompletionCallback
SmallVector<Result, 4> ExprResults;
SmallVector<Result, 1> EnumPatternTypes;

/// Add a result to \c Results, merging it with an existing result, if
/// possible.
void addExprResult(const Result &Res);

void sawSolutionImpl(const constraints::Solution &solution) override;

public:
Expand Down
5 changes: 3 additions & 2 deletions include/swift/Parse/IDEInspectionCallbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,9 @@ class CodeCompletionCallbacks {
/// Complete the \c in keyword in a for-each loop.
virtual void completeForEachInKeyword(){};

/// Complete a given expr-postfix.
virtual void completePostfixExpr(Expr *E, bool hasSpace) {};
/// Complete a expr-postfix. The \c CodeCompletionExpr has the expression it
/// is completing after set as its base.
virtual void completePostfixExpr(CodeCompletionExpr *E, bool hasSpace){};

/// Complete a given expr-postfix, given that there is a following
/// left parenthesis.
Expand Down
47 changes: 46 additions & 1 deletion include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,9 @@ class TypeVariableType::Implementation {
/// Determine whether this type variable represents a closure type.
bool isClosureType() const;

/// Determine whether this type variable represents a type of tap expression.
bool isTapType() const;

/// Determine whether this type variable represents one of the
/// parameter types associated with a closure.
bool isClosureParameterType() const;
Expand Down Expand Up @@ -935,6 +938,9 @@ enum ScoreKind: unsigned int {
/// A reference to an @unavailable declaration.
SK_Unavailable,
/// A reference to an async function in a synchronous context.
///
/// \note Any score kind after this is considered a conversion that doesn't
/// require fixing the source and will be ignored during code completion.
SK_AsyncInSyncMismatch,
/// Synchronous function in an asynchronous context or a conversion of
/// a synchronous function to an asynchronous one.
Expand Down Expand Up @@ -3951,6 +3957,20 @@ class ConstraintSystem {
/// \returns `true` if pack expansion has been resolved, `false` otherwise.
bool resolvePackExpansion(TypeVariableType *typeVar, Type contextualType);

/// Bind tap expression to the given contextual type and generate
/// constraints for its body.
///
/// \param typeVar The type variable representing the tap expression.
/// \param contextualType The contextual type this tap expression
/// would be bound to.
/// \param locator The locator associated with contextual type.
///
/// \returns `true` if it was possible to generate constraints for
/// the body and assign fixed type to the tap expression, `false`
/// otherwise.
bool resolveTapBody(TypeVariableType *typeVar, Type contextualType,
ConstraintLocatorBuilder locator);

/// Assign a fixed type to the given type variable.
///
/// \param typeVar The type variable to bind.
Expand Down Expand Up @@ -4354,6 +4374,14 @@ class ConstraintSystem {
FreeTypeVariableBinding allowFreeTypeVariables =
FreeTypeVariableBinding::Disallow);

/// Generate constraints for the body of the given tap expression.
///
/// \param tap the tap expression
///
/// \returns \c true if constraint generation failed, \c false otherwise
[[nodiscard]]
bool generateConstraints(TapExpr *tap);

/// Generate constraints for the body of the given function or closure.
///
/// \param fn The function or closure expression
Expand Down Expand Up @@ -5203,7 +5231,8 @@ class ConstraintSystem {
public:
/// Increase the score of the given kind for the current (partial) solution
/// along the.
void increaseScore(ScoreKind kind, unsigned value = 1);
void increaseScore(ScoreKind kind, ConstraintLocatorBuilder Locator,
unsigned value = 1);

/// Determine whether this solution is guaranteed to be worse than the best
/// solution found so far.
Expand Down Expand Up @@ -5285,6 +5314,22 @@ class ConstraintSystem {
llvm::Optional<SyntacticElementTarget>(SyntacticElementTarget)>
rewriteTarget);

/// Apply the given solution to the given tap expression.
///
/// \param solution The solution to apply.
/// \param tapExpr The tap expression to which the solution is being applied.
/// \param currentDC The declaration context in which transformations
/// will be applied.
/// \param rewriteTarget Function that performs a rewrite of any
/// solution application target within the context.
///
/// \returns true if solution cannot be applied.
bool applySolutionToBody(Solution &solution, TapExpr *tapExpr,
DeclContext *&currentDC,
std::function<llvm::Optional<SyntacticElementTarget>(
SyntacticElementTarget)>
rewriteTarget);

/// Reorder the disjunctive clauses for a given expression to
/// increase the likelihood that a favored constraint will be successfully
/// resolved before any others.
Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ SourceRange ASTNode::getSourceRange() const {
if (const auto *I = this->dyn_cast<CaseLabelItem *>()) {
return I->getSourceRange();
}
assert(!isNull() && "Null ASTNode doesn't have a source range");
llvm_unreachable("unsupported AST node");
}

Expand Down
13 changes: 12 additions & 1 deletion lib/IDE/ArgumentCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,18 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
for (auto Idx : range(0, ParamsToPass.size())) {
bool Optional = false;
if (Info.ValueRef) {
if (const ParamDecl *DeclParam = getParameterAt(Info.ValueRef, Idx)) {
if (Info.ValueRef.getDecl()->isInstanceMember() &&
!doesMemberRefApplyCurriedSelf(Info.BaseTy,
Info.ValueRef.getDecl())) {
// We are completing in an unapplied instance function, eg.
// struct TestStatic {
// func method() -> Void {}
// }
// TestStatic.method(#^STATIC^#)
// The 'self' parameter is never optional, so don't enter the check
// below (which always assumes that self has been applied).
} else if (const ParamDecl *DeclParam =
getParameterAt(Info.ValueRef, Idx)) {
Optional |= DeclParam->isDefaultArgument();
Optional |= DeclParam->getType()->is<PackExpansionType>();
}
Expand Down
Loading