Skip to content

[TypeChecker] Disable expression diagnostics for code completion #17980

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 2 commits into from
Jul 16, 2018
Merged
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
6 changes: 4 additions & 2 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8088,11 +8088,13 @@ bool ConstraintSystem::applySolutionFix(Expr *expr,
Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr,
Type convertType,
bool discardedExpr,
bool suppressDiagnostics,
bool skipClosures) {
// If any fixes needed to be applied to arrive at this solution, resolve
// them to specific expressions.
if (!solution.Fixes.empty()) {
if (shouldSuppressDiagnostics())
return nullptr;

// If we can diagnose the problem with the fixits that we've pre-assumed,
// do so now.
if (applySolutionFixes(expr, solution))
Expand All @@ -8108,7 +8110,7 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr,
for (auto &e : solution.Conformances)
TC.markConformanceUsed(e.second, DC);

ExprRewriter rewriter(*this, solution, suppressDiagnostics);
ExprRewriter rewriter(*this, solution, shouldSuppressDiagnostics());
ExprWalker walker(rewriter);

// Apply the solution to the expression.
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,7 @@ namespace {
if (!E->isActivated())
return Type();

CS.Options |= ConstraintSystemFlags::SuppressDiagnostics;
return CS.createTypeVariable(CS.getConstraintLocator(E),
TVO_CanBindToLValue);
}
Expand Down Expand Up @@ -3562,6 +3563,8 @@ class InferUnresolvedMemberConstraintGenerator : public ConstraintGenerator {
}

Type visitCodeCompletionExpr(CodeCompletionExpr *Expr) override {
auto &cs = getConstraintSystem();
cs.Options |= ConstraintSystemFlags::SuppressDiagnostics;
return createFreeTypeVariableType(Expr);
}

Expand Down
61 changes: 56 additions & 5 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1319,12 +1319,63 @@ void ConstraintSystem::shrink(Expr *expr) {
}
}

bool ConstraintSystem::solve(Expr *&expr,
Type convertType,
ExprTypeCheckListener *listener,
SmallVectorImpl<Solution> &solutions,
FreeTypeVariableBinding allowFreeTypeVariables) {
// Attempt to solve the constraint system.
auto solution = solveImpl(expr,
convertType,
listener,
solutions,
allowFreeTypeVariables);

// The constraint system has failed
if (solution == SolutionKind::Error)
return true;

// If the system is unsolved or there are multiple solutions present but
// type checker options do not allow unresolved types, let's try to salvage
if (solution == SolutionKind::Unsolved ||
(solutions.size() != 1 &&
!Options.contains(
ConstraintSystemFlags::AllowUnresolvedTypeVariables))) {
if (shouldSuppressDiagnostics())
return true;

// Try to provide a decent diagnostic.
if (salvage(solutions, expr)) {
// If salvage produced an error message, then it failed to salvage the
// expression, just bail out having reported the error.
return true;
}

// The system was salvaged; continue on as if nothing happened.
}

if (TC.getLangOpts().DebugConstraintSolver) {
auto &log = getASTContext().TypeCheckerDebug->getStream();
if (solutions.size() == 1) {
log << "---Solution---\n";
solutions[0].dump(log);
} else {
for (unsigned i = 0, e = solutions.size(); i != e; ++i) {
log << "--- Solution #" << i << " ---\n";
solutions[i].dump(log);
}
}
}

return false;
}

ConstraintSystem::SolutionKind
ConstraintSystem::solve(Expr *&expr,
Type convertType,
ExprTypeCheckListener *listener,
SmallVectorImpl<Solution> &solutions,
FreeTypeVariableBinding allowFreeTypeVariables) {
ConstraintSystem::solveImpl(Expr *&expr,
Type convertType,
ExprTypeCheckListener *listener,
SmallVectorImpl<Solution> &solutions,
FreeTypeVariableBinding allowFreeTypeVariables) {
if (TC.getLangOpts().DebugConstraintSolver) {
auto &log = getASTContext().TypeCheckerDebug->getStream();
log << "---Constraint solving for the expression at ";
Expand Down
50 changes: 42 additions & 8 deletions lib/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,16 @@ enum class ConstraintSystemFlags {
/// If set, this is going to prevent constraint system from erasing all
/// discovered solutions except the best one.
ReturnAllDiscoveredSolutions = 0x04,

/// Set if the client wants diagnostics suppressed.
SuppressDiagnostics = 0x08,

/// If set, the client wants a best-effort solution to the constraint system,
/// but can tolerate a solution where all of the constraints are solved, but
/// not all type variables have been determined. In this case, the constraint
/// system is not applied to the expression AST, but the ConstraintSystem is
/// left in-tact.
AllowUnresolvedTypeVariables = 0x10,
};

/// Options that affect the constraint system as a whole.
Expand Down Expand Up @@ -1709,6 +1719,10 @@ class ConstraintSystem {
return !solverState || solverState->recordFixes;
}

bool shouldSuppressDiagnostics() {
return Options.contains(ConstraintSystemFlags::SuppressDiagnostics);
}

/// \brief Log and record the application of the fix. Return true iff any
/// subsequent solution would be worse than the best known solution.
bool recordFix(Fix fix, ConstraintLocatorBuilder locator);
Expand Down Expand Up @@ -2962,7 +2976,6 @@ class ConstraintSystem {
bool *foundConsistent);
bool areBindPairConsistent(Constraint *first, Constraint *second);

public:
/// \brief Solve the system of constraints generated from provided expression.
///
/// \param expr The expression to generate constraints from.
Expand All @@ -2974,12 +2987,34 @@ class ConstraintSystem {
///
/// \returns Error is an error occurred, Solved is system is consistent
/// and solutions were found, Unsolved otherwise.
SolutionKind solve(Expr *&expr,
Type convertType,
ExprTypeCheckListener *listener,
SmallVectorImpl<Solution> &solutions,
FreeTypeVariableBinding allowFreeTypeVariables
= FreeTypeVariableBinding::Disallow);
SolutionKind solveImpl(Expr *&expr,
Type convertType,
ExprTypeCheckListener *listener,
SmallVectorImpl<Solution> &solutions,
FreeTypeVariableBinding allowFreeTypeVariables
= FreeTypeVariableBinding::Disallow);

public:
/// \brief Solve the system of constraints generated from provided expression.
///
/// The expression should have already been pre-checked with
/// preCheckExpression().
///
/// \param expr The expression to generate constraints from.
/// \param convertType The expected type of the expression.
/// \param listener The callback to check solving progress.
/// \param solutions The set of solutions to the system of constraints.
/// \param allowFreeTypeVariables How to bind free type variables in
/// the solution.
///
/// \returns true is an error occurred, false is system is consistent
/// and solutions were found.
bool solve(Expr *&expr,
Type convertType,
ExprTypeCheckListener *listener,
SmallVectorImpl<Solution> &solutions,
FreeTypeVariableBinding allowFreeTypeVariables
= FreeTypeVariableBinding::Disallow);

/// \brief Solve the system of constraints.
///
Expand Down Expand Up @@ -3067,7 +3102,6 @@ class ConstraintSystem {
/// non-single expression closures.
Expr *applySolution(Solution &solution, Expr *expr,
Type convertType, bool discardedExpr,
bool suppressDiagnostics,
bool skipClosures);

/// \brief Apply a given solution to the expression to the top-level
Expand Down
92 changes: 21 additions & 71 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1909,57 +1909,6 @@ bool GenericRequirementsCheckListener::diagnoseUnsatisfiedRequirement(
return false;
}

bool TypeChecker::
solveForExpression(Expr *&expr, DeclContext *dc, Type convertType,
FreeTypeVariableBinding allowFreeTypeVariables,
ExprTypeCheckListener *listener, ConstraintSystem &cs,
SmallVectorImpl<Solution> &viable,
TypeCheckExprOptions options) {
// Attempt to solve the constraint system.
auto solution = cs.solve(expr,
convertType,
listener,
viable,
allowFreeTypeVariables);

// The constraint system has failed
if (solution == ConstraintSystem::SolutionKind::Error)
return true;

// If the system is unsolved or there are multiple solutions present but
// type checker options do not allow unresolved types, let's try to salvage
if (solution == ConstraintSystem::SolutionKind::Unsolved
|| (viable.size() != 1 &&
!options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables))) {
if (options.contains(TypeCheckExprFlags::SuppressDiagnostics))
return true;

// Try to provide a decent diagnostic.
if (cs.salvage(viable, expr)) {
// If salvage produced an error message, then it failed to salvage the
// expression, just bail out having reported the error.
return true;
}

// The system was salvaged; continue on as if nothing happened.
}

if (getLangOpts().DebugConstraintSolver) {
auto &log = Context.TypeCheckerDebug->getStream();
if (viable.size() == 1) {
log << "---Solution---\n";
viable[0].dump(log);
} else {
for (unsigned i = 0, e = viable.size(); i != e; ++i) {
log << "--- Solution #" << i << " ---\n";
viable[i].dump(log);
}
}
}

return false;
}

#pragma mark High-level entry points
Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
TypeLoc convertType,
Expand All @@ -1976,6 +1925,13 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,

// Construct a constraint system from this expression.
ConstraintSystemOptions csOptions = ConstraintSystemFlags::AllowFixes;

if (options.contains(TypeCheckExprFlags::SuppressDiagnostics))
csOptions |= ConstraintSystemFlags::SuppressDiagnostics;

if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables))
csOptions |= ConstraintSystemFlags::AllowUnresolvedTypeVariables;

ConstraintSystem cs(*this, dc, csOptions);
cs.baseCS = baseCS;
CleanupIllFormedExpressionRAII cleanup(expr);
Expand Down Expand Up @@ -2007,9 +1963,6 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
// that we don't later treat it as an actual conversion constraint.
if (options.contains(TypeCheckExprFlags::ConvertTypeIsOnlyAHint))
convertType = TypeLoc();

bool suppressDiagnostics =
options.contains(TypeCheckExprFlags::SuppressDiagnostics);

// If the client can handle unresolved type variables, leave them in the
// system.
Expand All @@ -2019,8 +1972,8 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,

// Attempt to solve the constraint system.
SmallVector<Solution, 4> viable;
if (solveForExpression(expr, dc, convertType.getType(),
allowFreeTypeVariables, listener, cs, viable, options))
if (cs.solve(expr, convertType.getType(), listener, viable,
allowFreeTypeVariables))
return Type();

// If the client allows the solution to have unresolved type expressions,
Expand All @@ -2046,11 +1999,10 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
}

// Apply the solution to the expression.
bool isDiscarded = options.contains(TypeCheckExprFlags::IsDiscarded);
bool skipClosures = options.contains(TypeCheckExprFlags::SkipMultiStmtClosures);
result = cs.applySolution(solution, result, convertType.getType(),
isDiscarded, suppressDiagnostics,
skipClosures);
result = cs.applySolution(
solution, result, convertType.getType(),
options.contains(TypeCheckExprFlags::IsDiscarded),
options.contains(TypeCheckExprFlags::SkipMultiStmtClosures));
if (!result) {
// Failure already diagnosed, above, as part of applying the solution.
return Type();
Expand All @@ -2072,7 +2024,7 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,

// Unless the client has disabled them, perform syntactic checks on the
// expression now.
if (!suppressDiagnostics &&
if (!cs.shouldSuppressDiagnostics() &&
!options.contains(TypeCheckExprFlags::DisableStructuralChecks)) {
bool isExprStmt = options.contains(TypeCheckExprFlags::IsExprStmt);
performSyntacticExprDiagnostics(*this, result, dc, isExprStmt);
Expand All @@ -2092,7 +2044,7 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc,
referencedDecl = nullptr;

// Construct a constraint system from this expression.
ConstraintSystem cs(*this, dc, ConstraintSystemFlags::AllowFixes);
ConstraintSystem cs(*this, dc, ConstraintSystemFlags::SuppressDiagnostics);
CleanupIllFormedExpressionRAII cleanup(expr);

// Attempt to solve the constraint system.
Expand All @@ -2108,9 +2060,8 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc,
// re-check.
if (needClearType)
expr->setType(Type());
if (solveForExpression(expr, dc, /*convertType*/Type(),
allowFreeTypeVariables, listener, cs, viable,
TypeCheckExprFlags::SuppressDiagnostics)) {
if (cs.solve(expr, /*convertType*/Type(), listener, viable,
allowFreeTypeVariables)) {
recoverOriginalType();
return Type();
}
Expand Down Expand Up @@ -2177,8 +2128,8 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying(

// Construct a constraint system from this expression.
ConstraintSystemOptions options;
options |= ConstraintSystemFlags::AllowFixes;
options |= ConstraintSystemFlags::ReturnAllDiscoveredSolutions;
options |= ConstraintSystemFlags::SuppressDiagnostics;

ConstraintSystem cs(*this, dc, options);

Expand All @@ -2194,9 +2145,8 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying(
if (originalType && originalType->hasError())
expr->setType(Type());

solveForExpression(expr, dc, /*convertType*/ Type(), allowFreeTypeVariables,
listener, cs, viable,
TypeCheckExprFlags::SuppressDiagnostics);
cs.solve(expr, /*convertType*/ Type(), listener, viable,
allowFreeTypeVariables);
}

for (auto &solution : viable) {
Expand All @@ -2210,7 +2160,7 @@ bool TypeChecker::typeCheckCompletionSequence(Expr *&expr, DeclContext *DC) {
PrettyStackTraceExpr stackTrace(Context, "type-checking", expr);

// Construct a constraint system from this expression.
ConstraintSystem CS(*this, DC, ConstraintSystemFlags::AllowFixes);
ConstraintSystem CS(*this, DC, ConstraintSystemFlags::SuppressDiagnostics);
CleanupIllFormedExpressionRAII cleanup(expr);

auto *SE = cast<SequenceExpr>(expr);
Expand Down
8 changes: 6 additions & 2 deletions lib/Sema/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -900,13 +900,17 @@ bool swift::typeCheckExpression(DeclContext *DC, Expr *&parsedExpr) {
auto &ctx = DC->getASTContext();
if (ctx.getLazyResolver()) {
TypeChecker *TC = static_cast<TypeChecker *>(ctx.getLazyResolver());
auto resultTy = TC->typeCheckExpression(parsedExpr, DC);
auto resultTy = TC->typeCheckExpression(parsedExpr, DC, TypeLoc(),
ContextualTypePurpose::CTP_Unused,
TypeCheckExprFlags::SuppressDiagnostics);
return !resultTy;
} else {
// Set up a diagnostics engine that swallows diagnostics.
DiagnosticEngine diags(ctx.SourceMgr);
TypeChecker TC(ctx, diags);
auto resultTy = TC.typeCheckExpression(parsedExpr, DC);
auto resultTy = TC.typeCheckExpression(parsedExpr, DC, TypeLoc(),
ContextualTypePurpose::CTP_Unused,
TypeCheckExprFlags::SuppressDiagnostics);
return !resultTy;
}
}
Expand Down
Loading