[Diagnostics] Emit a warning when an immutable decodable property has an initial value (#30218)

* [Diagnostics] Emit a warning when an immutable decodable property has an initial value

* [Sema] Use Decl::diagnose instead of Diags.diagnose

* [AST] Remove property name from 'decodable_property_will_not_be_decoded' diagnostic

* [Test] Update tests

* [Test] Update existing codable tests
This commit is contained in:
Suyash Srijan
2020-03-31 23:16:08 +01:00
committed by GitHub
parent efa1a01a5a
commit 95f0651cbb
8 changed files with 194 additions and 2 deletions

View File

@@ -830,9 +830,67 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) {
std::tie(varDecl, varType, useIfPresentVariant) =
lookupVarDeclForCodingKeysCase(conformanceDC, elt, targetDecl);
// Don't output a decode statement for a var let with a default value.
if (varDecl->isLet() && varDecl->isParentInitialized())
// Don't output a decode statement for a let with an initial value.
if (varDecl->isLet() && varDecl->isParentInitialized()) {
// But emit a warning to let the user know that it won't be decoded.
auto lookupResult =
codingKeysEnum->lookupDirect(varDecl->getBaseName());
auto keyExistsInCodingKeys =
llvm::any_of(lookupResult, [&](ValueDecl *VD) {
if (isa<EnumElementDecl>(VD)) {
return VD->getBaseName() == varDecl->getBaseName();
}
return false;
});
auto *encodableProto = C.getProtocol(KnownProtocolKind::Encodable);
bool conformsToEncodable =
TypeChecker::conformsToProtocol(
targetDecl->getDeclaredInterfaceType(), encodableProto,
conformanceDC,
ConformanceCheckFlags::SkipConditionalRequirements) != nullptr;
// Strategy to use for CodingKeys enum diagnostic part - this is to
// make the behaviour more explicit:
//
// 1. If we have an *implicit* CodingKeys enum:
// (a) If the type is Decodable only, explicitly define the enum and
// remove the key from it. This makes it explicit that the key
// will not be decoded.
// (b) If the type is Codable, explicitly define the enum and keep the
// key in it. This is because removing the key will break encoding
// which is mostly likely not what the user expects.
//
// 2. If we have an *explicit* CodingKeys enum:
// (a) If the type is Decodable only and the key exists in the enum,
// then explicitly remove the key from the enum. This makes it
// explicit that the key will not be decoded.
// (b) If the type is Decodable only and the key does not exist in
// the enum, do nothing. This is because the user has explicitly
// made it clear that that they don't want the key to be decoded.
// (c) If the type is Codable, do nothing. This is because removing
// the key will break encoding which is most likely not what the
// user expects.
if (!codingKeysEnum->isImplicit()) {
if (conformsToEncodable || !keyExistsInCodingKeys) {
continue;
}
}
varDecl->diagnose(diag::decodable_property_will_not_be_decoded);
if (codingKeysEnum->isImplicit()) {
varDecl->diagnose(
diag::decodable_property_init_or_codingkeys_implicit,
conformsToEncodable ? 0 : 1, varDecl->getName());
} else {
varDecl->diagnose(
diag::decodable_property_init_or_codingkeys_explicit,
varDecl->getName());
}
varDecl->diagnose(diag::decodable_make_property_mutable)
.fixItReplace(varDecl->getAttributeInsertionLoc(true), "var");
continue;
}
auto methodName =
useIfPresentVariant ? C.Id_decodeIfPresent : C.Id_decode;