Move helper function 'diagnoseArgumentLabelError' to MiscDiagnostics.

Groundwork for SR-1008. No functionality change.
This commit is contained in:
Jordan Rose
2016-04-28 16:04:24 -07:00
parent d7b3b6a462
commit ec59bf9f18
3 changed files with 197 additions and 172 deletions

View File

@@ -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.

View File

@@ -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.
//===----------------------------------------------------------------------===//

View File

@@ -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