mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Move helper function 'diagnoseArgumentLabelError' to MiscDiagnostics.
Groundwork for SR-1008. No functionality change.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ConstraintSystem.h"
|
||||
#include "MiscDiagnostics.h"
|
||||
#include "swift/AST/ASTWalker.h"
|
||||
#include "swift/AST/TypeWalker.h"
|
||||
#include "swift/AST/TypeMatcher.h"
|
||||
@@ -3523,173 +3524,6 @@ typeCheckArgumentChildIndependently(Expr *argExpr, Type argType,
|
||||
}
|
||||
|
||||
|
||||
/// Diagnose an argument labeling issue, returning true if we successfully
|
||||
/// diagnosed the issue.
|
||||
static bool diagnoseArgumentLabelError(Expr *expr,
|
||||
ArrayRef<Identifier> newNames,
|
||||
bool isSubscript, ConstraintSystem &CS) {
|
||||
|
||||
auto tuple = dyn_cast<TupleExpr>(expr);
|
||||
if (!tuple) {
|
||||
if (newNames[0].empty()) {
|
||||
// This is probably a conversion from a value of labeled tuple type to
|
||||
// a scalar.
|
||||
// FIXME: We want this issue to disappear completely when single-element
|
||||
// labelled tuples go away.
|
||||
if (auto tupleTy = expr->getType()->getRValueType()->getAs<TupleType>()) {
|
||||
int scalarFieldIdx = tupleTy->getElementForScalarInit();
|
||||
if (scalarFieldIdx >= 0) {
|
||||
auto &field = tupleTy->getElement(scalarFieldIdx);
|
||||
if (field.hasName()) {
|
||||
llvm::SmallString<16> str;
|
||||
str = ".";
|
||||
str += field.getName().str();
|
||||
CS.TC.diagnose(expr->getStartLoc(),
|
||||
diag::extra_named_single_element_tuple,
|
||||
field.getName().str())
|
||||
.fixItInsertAfter(expr->getEndLoc(), str);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't know what to do with this.
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is a scalar-to-tuple conversion. Add the name. We "know"
|
||||
// that we're inside a ParenExpr, because ParenExprs are required
|
||||
// by the syntax and locator resolution looks through on level of
|
||||
// them.
|
||||
|
||||
// Look through the paren expression, if there is one.
|
||||
if (auto parenExpr = dyn_cast<ParenExpr>(expr))
|
||||
expr = parenExpr->getSubExpr();
|
||||
|
||||
llvm::SmallString<16> str;
|
||||
str += newNames[0].str();
|
||||
str += ": ";
|
||||
CS.TC.diagnose(expr->getStartLoc(), diag::missing_argument_labels, false,
|
||||
str.substr(0, str.size()-1), isSubscript)
|
||||
.fixItInsert(expr->getStartLoc(), str);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Figure out how many extraneous, missing, and wrong labels are in
|
||||
// the call.
|
||||
unsigned numExtra = 0, numMissing = 0, numWrong = 0;
|
||||
unsigned n = std::max(tuple->getNumElements(), (unsigned)newNames.size());
|
||||
|
||||
llvm::SmallString<16> missingBuffer;
|
||||
llvm::SmallString<16> extraBuffer;
|
||||
for (unsigned i = 0; i != n; ++i) {
|
||||
Identifier oldName;
|
||||
if (i < tuple->getNumElements())
|
||||
oldName = tuple->getElementName(i);
|
||||
Identifier newName;
|
||||
if (i < newNames.size())
|
||||
newName = newNames[i];
|
||||
|
||||
if (oldName == newName ||
|
||||
(tuple->hasTrailingClosure() && i == tuple->getNumElements()-1))
|
||||
continue;
|
||||
|
||||
if (oldName.empty()) {
|
||||
++numMissing;
|
||||
missingBuffer += newName.str();
|
||||
missingBuffer += ":";
|
||||
} else if (newName.empty()) {
|
||||
++numExtra;
|
||||
extraBuffer += oldName.str();
|
||||
extraBuffer += ':';
|
||||
} else
|
||||
++numWrong;
|
||||
}
|
||||
|
||||
// Emit the diagnostic.
|
||||
assert(numMissing > 0 || numExtra > 0 || numWrong > 0);
|
||||
llvm::SmallString<16> haveBuffer; // note: diagOpt has references to this
|
||||
llvm::SmallString<16> expectedBuffer; // note: diagOpt has references to this
|
||||
Optional<InFlightDiagnostic> diagOpt;
|
||||
|
||||
// If we had any wrong labels, or we have both missing and extra labels,
|
||||
// emit the catch-all "wrong labels" diagnostic.
|
||||
bool plural = (numMissing + numExtra + numWrong) > 1;
|
||||
if (numWrong > 0 || (numMissing > 0 && numExtra > 0)) {
|
||||
for (unsigned i = 0, n = tuple->getNumElements(); i != n; ++i) {
|
||||
auto haveName = tuple->getElementName(i);
|
||||
if (haveName.empty())
|
||||
haveBuffer += '_';
|
||||
else
|
||||
haveBuffer += haveName.str();
|
||||
haveBuffer += ':';
|
||||
}
|
||||
|
||||
for (auto expected : newNames) {
|
||||
if (expected.empty())
|
||||
expectedBuffer += '_';
|
||||
else
|
||||
expectedBuffer += expected.str();
|
||||
expectedBuffer += ':';
|
||||
}
|
||||
|
||||
StringRef haveStr = haveBuffer;
|
||||
StringRef expectedStr = expectedBuffer;
|
||||
diagOpt.emplace(CS.TC.diagnose(expr->getLoc(), diag::wrong_argument_labels,
|
||||
plural, haveStr, expectedStr, isSubscript));
|
||||
} else if (numMissing > 0) {
|
||||
StringRef missingStr = missingBuffer;
|
||||
diagOpt.emplace(CS.TC.diagnose(expr->getLoc(),diag::missing_argument_labels,
|
||||
plural, missingStr, isSubscript));
|
||||
} else {
|
||||
assert(numExtra > 0);
|
||||
StringRef extraStr = extraBuffer;
|
||||
diagOpt.emplace(CS.TC.diagnose(expr->getLoc(), diag::extra_argument_labels,
|
||||
plural, extraStr, isSubscript));
|
||||
}
|
||||
|
||||
// Emit Fix-Its to correct the names.
|
||||
auto &diag = *diagOpt;
|
||||
for (unsigned i = 0, n = tuple->getNumElements(); i != n; ++i) {
|
||||
Identifier oldName = tuple->getElementName(i);
|
||||
Identifier newName;
|
||||
if (i < newNames.size())
|
||||
newName = newNames[i];
|
||||
|
||||
if (oldName == newName || (i == n-1 && tuple->hasTrailingClosure()))
|
||||
continue;
|
||||
|
||||
if (newName.empty()) {
|
||||
// Delete the old name.
|
||||
diag.fixItRemoveChars(tuple->getElementNameLocs()[i],
|
||||
tuple->getElement(i)->getStartLoc());
|
||||
continue;
|
||||
}
|
||||
|
||||
bool newNameIsReserved = !canBeArgumentLabel(newName.str());
|
||||
llvm::SmallString<16> newStr;
|
||||
if (newNameIsReserved)
|
||||
newStr += "`";
|
||||
newStr += newName.str();
|
||||
if (newNameIsReserved)
|
||||
newStr += "`";
|
||||
|
||||
if (oldName.empty()) {
|
||||
// Insert the name.
|
||||
newStr += ": ";
|
||||
diag.fixItInsert(tuple->getElement(i)->getStartLoc(), newStr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Change the name.
|
||||
diag.fixItReplace(tuple->getElementNameLocs()[i], newStr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Emit a class of diagnostics that we only know how to generate when there is
|
||||
/// exactly one candidate we know about. Return true if an error is emitted.
|
||||
static bool diagnoseSingleCandidateFailures(CalleeCandidateInfo &CCI,
|
||||
@@ -3835,9 +3669,8 @@ static bool diagnoseSingleCandidateFailures(CalleeCandidateInfo &CCI,
|
||||
|
||||
// If this is an argument label mismatch, then diagnose that error now.
|
||||
if (!correctNames.empty() &&
|
||||
diagnoseArgumentLabelError(argExpr, correctNames,
|
||||
/*isSubscript=*/isa<SubscriptExpr>(fnExpr),
|
||||
*CCI.CS))
|
||||
diagnoseArgumentLabelError(TC, argExpr, correctNames,
|
||||
isa<SubscriptExpr>(fnExpr)))
|
||||
return true;
|
||||
|
||||
// If we have an out-of-order argument, diagnose it as such.
|
||||
@@ -5188,8 +5021,8 @@ bool FailureDiagnosis::visitUnresolvedMemberExpr(UnresolvedMemberExpr *E) {
|
||||
for (auto &arg : arguments)
|
||||
expectedNames.push_back(arg.Label);
|
||||
|
||||
return diagnoseArgumentLabelError(argExpr, expectedNames,
|
||||
/*isSubscript*/false, *CS);
|
||||
return diagnoseArgumentLabelError(CS->TC, argExpr, expectedNames,
|
||||
/*isSubscript*/false);
|
||||
}
|
||||
|
||||
case CC_GeneralMismatch: // Something else is wrong.
|
||||
|
||||
@@ -811,6 +811,185 @@ static void diagnoseImplicitSelfUseInClosure(TypeChecker &TC, const Expr *E,
|
||||
const_cast<Expr *>(E)->walk(DiagnoseWalker(TC, isAlreadyInClosure));
|
||||
}
|
||||
|
||||
/// Diagnose an argument labeling issue, returning true if we successfully
|
||||
/// diagnosed the issue.
|
||||
bool swift::diagnoseArgumentLabelError(TypeChecker &TC, const Expr *expr,
|
||||
ArrayRef<Identifier> newNames,
|
||||
bool isSubscript,
|
||||
InFlightDiagnostic *existingDiag) {
|
||||
Optional<InFlightDiagnostic> diagOpt;
|
||||
auto getDiag = [&]() -> InFlightDiagnostic & {
|
||||
if (existingDiag)
|
||||
return *existingDiag;
|
||||
return *diagOpt;
|
||||
};
|
||||
|
||||
auto tuple = dyn_cast<TupleExpr>(expr);
|
||||
if (!tuple) {
|
||||
if (newNames[0].empty()) {
|
||||
// This is probably a conversion from a value of labeled tuple type to
|
||||
// a scalar.
|
||||
// FIXME: We want this issue to disappear completely when single-element
|
||||
// labelled tuples go away.
|
||||
if (auto tupleTy = expr->getType()->getRValueType()->getAs<TupleType>()) {
|
||||
int scalarFieldIdx = tupleTy->getElementForScalarInit();
|
||||
if (scalarFieldIdx >= 0) {
|
||||
auto &field = tupleTy->getElement(scalarFieldIdx);
|
||||
if (field.hasName()) {
|
||||
llvm::SmallString<16> str;
|
||||
str = ".";
|
||||
str += field.getName().str();
|
||||
if (!existingDiag) {
|
||||
diagOpt.emplace(TC.diagnose(expr->getStartLoc(),
|
||||
diag::extra_named_single_element_tuple,
|
||||
field.getName().str()));
|
||||
}
|
||||
getDiag().fixItInsertAfter(expr->getEndLoc(), str);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't know what to do with this.
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is a scalar-to-tuple conversion. Add the name. We "know"
|
||||
// that we're inside a ParenExpr, because ParenExprs are required
|
||||
// by the syntax and locator resolution looks through on level of
|
||||
// them.
|
||||
|
||||
// Look through the paren expression, if there is one.
|
||||
if (auto parenExpr = dyn_cast<ParenExpr>(expr))
|
||||
expr = parenExpr->getSubExpr();
|
||||
|
||||
llvm::SmallString<16> str;
|
||||
str += newNames[0].str();
|
||||
str += ": ";
|
||||
if (!existingDiag) {
|
||||
diagOpt.emplace(TC.diagnose(expr->getStartLoc(),
|
||||
diag::missing_argument_labels,
|
||||
false, str.str().drop_back(), isSubscript));
|
||||
}
|
||||
getDiag().fixItInsert(expr->getStartLoc(), str);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Figure out how many extraneous, missing, and wrong labels are in
|
||||
// the call.
|
||||
unsigned numExtra = 0, numMissing = 0, numWrong = 0;
|
||||
unsigned n = std::max(tuple->getNumElements(), (unsigned)newNames.size());
|
||||
|
||||
llvm::SmallString<16> missingBuffer;
|
||||
llvm::SmallString<16> extraBuffer;
|
||||
for (unsigned i = 0; i != n; ++i) {
|
||||
Identifier oldName;
|
||||
if (i < tuple->getNumElements())
|
||||
oldName = tuple->getElementName(i);
|
||||
Identifier newName;
|
||||
if (i < newNames.size())
|
||||
newName = newNames[i];
|
||||
|
||||
if (oldName == newName ||
|
||||
(tuple->hasTrailingClosure() && i == tuple->getNumElements()-1))
|
||||
continue;
|
||||
|
||||
if (oldName.empty()) {
|
||||
++numMissing;
|
||||
missingBuffer += newName.str();
|
||||
missingBuffer += ":";
|
||||
} else if (newName.empty()) {
|
||||
++numExtra;
|
||||
extraBuffer += oldName.str();
|
||||
extraBuffer += ':';
|
||||
} else
|
||||
++numWrong;
|
||||
}
|
||||
|
||||
// Emit the diagnostic.
|
||||
assert(numMissing > 0 || numExtra > 0 || numWrong > 0);
|
||||
llvm::SmallString<16> haveBuffer; // note: diagOpt has references to this
|
||||
llvm::SmallString<16> expectedBuffer; // note: diagOpt has references to this
|
||||
|
||||
// If we had any wrong labels, or we have both missing and extra labels,
|
||||
// emit the catch-all "wrong labels" diagnostic.
|
||||
if (!existingDiag) {
|
||||
bool plural = (numMissing + numExtra + numWrong) > 1;
|
||||
if (numWrong > 0 || (numMissing > 0 && numExtra > 0)) {
|
||||
for (unsigned i = 0, n = tuple->getNumElements(); i != n; ++i) {
|
||||
auto haveName = tuple->getElementName(i);
|
||||
if (haveName.empty())
|
||||
haveBuffer += '_';
|
||||
else
|
||||
haveBuffer += haveName.str();
|
||||
haveBuffer += ':';
|
||||
}
|
||||
|
||||
for (auto expected : newNames) {
|
||||
if (expected.empty())
|
||||
expectedBuffer += '_';
|
||||
else
|
||||
expectedBuffer += expected.str();
|
||||
expectedBuffer += ':';
|
||||
}
|
||||
|
||||
StringRef haveStr = haveBuffer;
|
||||
StringRef expectedStr = expectedBuffer;
|
||||
diagOpt.emplace(TC.diagnose(expr->getLoc(), diag::wrong_argument_labels,
|
||||
plural, haveStr, expectedStr, isSubscript));
|
||||
} else if (numMissing > 0) {
|
||||
StringRef missingStr = missingBuffer;
|
||||
diagOpt.emplace(TC.diagnose(expr->getLoc(), diag::missing_argument_labels,
|
||||
plural, missingStr, isSubscript));
|
||||
} else {
|
||||
assert(numExtra > 0);
|
||||
StringRef extraStr = extraBuffer;
|
||||
diagOpt.emplace(TC.diagnose(expr->getLoc(), diag::extra_argument_labels,
|
||||
plural, extraStr, isSubscript));
|
||||
}
|
||||
}
|
||||
|
||||
// Emit Fix-Its to correct the names.
|
||||
auto &diag = getDiag();
|
||||
for (unsigned i = 0, n = tuple->getNumElements(); i != n; ++i) {
|
||||
Identifier oldName = tuple->getElementName(i);
|
||||
Identifier newName;
|
||||
if (i < newNames.size())
|
||||
newName = newNames[i];
|
||||
|
||||
if (oldName == newName || (i == n-1 && tuple->hasTrailingClosure()))
|
||||
continue;
|
||||
|
||||
if (newName.empty()) {
|
||||
// Delete the old name.
|
||||
diag.fixItRemoveChars(tuple->getElementNameLocs()[i],
|
||||
tuple->getElement(i)->getStartLoc());
|
||||
continue;
|
||||
}
|
||||
|
||||
bool newNameIsReserved = !canBeArgumentLabel(newName.str());
|
||||
llvm::SmallString<16> newStr;
|
||||
if (newNameIsReserved)
|
||||
newStr += "`";
|
||||
newStr += newName.str();
|
||||
if (newNameIsReserved)
|
||||
newStr += "`";
|
||||
|
||||
if (oldName.empty()) {
|
||||
// Insert the name.
|
||||
newStr += ": ";
|
||||
diag.fixItInsert(tuple->getElement(i)->getStartLoc(), newStr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Change the name.
|
||||
diag.fixItReplace(tuple->getElementNameLocs()[i], newStr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Diagnose availability.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
#define SWIFT_SEMA_MISC_DIAGNOSTICS_H
|
||||
|
||||
#include "swift/AST/AttrKind.h"
|
||||
#include "swift/AST/Identifier.h"
|
||||
#include "swift/Basic/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
|
||||
namespace swift {
|
||||
class AbstractFunctionDecl;
|
||||
@@ -41,6 +45,15 @@ void performAbstractFuncDeclDiagnostics(TypeChecker &TC,
|
||||
void fixItAccessibility(InFlightDiagnostic &diag, ValueDecl *VD,
|
||||
Accessibility desiredAccess, bool isForSetter = false);
|
||||
|
||||
/// Emit fix-its to correct the argument labels in \p expr, which is the
|
||||
/// argument tuple or single argument of a call.
|
||||
///
|
||||
/// If \p existingDiag is null, the fix-its will be attached to an appropriate
|
||||
/// error diagnostic.
|
||||
bool diagnoseArgumentLabelError(TypeChecker &TC, const Expr *expr,
|
||||
ArrayRef<Identifier> newNames,
|
||||
bool isSubscript,
|
||||
InFlightDiagnostic *existingDiag = nullptr);
|
||||
} // namespace swift
|
||||
|
||||
#endif // SWIFT_SEMA_MISC_DIAGNOSTICS_H
|
||||
|
||||
Reference in New Issue
Block a user