@@ -5011,6 +5011,163 @@ static void diagUnqualifiedAccessToMethodNamedSelf(const Expr *E,
50115011 const_cast <Expr *>(E)->walk (Walker);
50125012}
50135013
5014+ static void
5015+ diagnoseDictionaryLiteralDuplicateKeyEntries (const Expr *E,
5016+ const DeclContext *DC) {
5017+ class DiagnoseWalker : public ASTWalker {
5018+ ASTContext &Ctx;
5019+
5020+ private:
5021+ std::string getKeyStringValue (const LiteralExpr *keyExpr) {
5022+ if (isa<MagicIdentifierLiteralExpr>(keyExpr)) {
5023+ return keyExpr->getLiteralKindDescription ().str ();
5024+ }
5025+ std::string out;
5026+ llvm::raw_string_ostream OS (out);
5027+ keyExpr->printConstExprValue (&OS, /* additionalCheck=*/ nullptr );
5028+ return out;
5029+ }
5030+
5031+ std::string getKeyStringValueForDiagnostic (const LiteralExpr *keyExpr) {
5032+ std::string out;
5033+ switch (keyExpr->getKind ()) {
5034+ case ExprKind::NilLiteral:
5035+ case ExprKind::MagicIdentifierLiteral:
5036+ return out;
5037+ case ExprKind::StringLiteral: {
5038+ const auto *SL = cast<StringLiteralExpr>(keyExpr);
5039+ out = SL->getValue ().str ();
5040+ break ;
5041+ }
5042+ default :
5043+ llvm::raw_string_ostream OS (out);
5044+ keyExpr->printConstExprValue (&OS, /* additionalCheck=*/ nullptr );
5045+ break ;
5046+ }
5047+ return " '" + out + " '" ;
5048+ }
5049+
5050+ bool shouldDiagnoseLiteral (const LiteralExpr *LE) {
5051+ switch (LE->getKind ()) {
5052+ case ExprKind::IntegerLiteral:
5053+ case ExprKind::FloatLiteral:
5054+ case ExprKind::BooleanLiteral:
5055+ case ExprKind::StringLiteral:
5056+ case ExprKind::MagicIdentifierLiteral:
5057+ case ExprKind::NilLiteral:
5058+ return true ;
5059+ // Skip interpolated literals because they
5060+ // can contain expressions that although equal
5061+ // maybe be evaluated to different values. e.g.
5062+ // "\(a) \(a)" where 'a' is a computed variable.
5063+ case ExprKind::InterpolatedStringLiteral:
5064+ // Also skip object literals as most of them takes paramenters that can
5065+ // contain expressions that altough equal may evaluate to different
5066+ // values e.g. #fileLiteral(resourceName: a) where 'a' is a computed
5067+ // property is valid.
5068+ case ExprKind::ObjectLiteral:
5069+ // Literal expressions produce Regex<Out> type result,
5070+ // which cannot be keys due to not conforming to hashable.
5071+ case ExprKind::RegexLiteral:
5072+ return false ;
5073+ // If a new literal is added in the future, the compiler
5074+ // will warn that a case is missing from this switch.
5075+ #define LITERAL_EXPR (Id, Parent )
5076+ #define EXPR (Id, Parent ) case ExprKind::Id:
5077+ #include " swift/AST/ExprNodes.def"
5078+ llvm_unreachable (" Not a literal expression" );
5079+ }
5080+ llvm_unreachable (" Unhandled literal" );
5081+ }
5082+ public:
5083+ DiagnoseWalker (const DeclContext *DC) : Ctx(DC->getASTContext ()) {}
5084+
5085+ bool shouldWalkIntoSeparatelyCheckedClosure (ClosureExpr *expr) override {
5086+ return false ;
5087+ }
5088+
5089+ bool shouldWalkIntoTapExpression () override { return false ; }
5090+
5091+ std::pair<bool , Expr *> walkToExprPre (Expr *E) override {
5092+ const auto *DLE = dyn_cast_or_null<DictionaryExpr>(E);
5093+ if (!DLE)
5094+ return {true , E};
5095+
5096+ auto type = DLE->getType ();
5097+ // For other types conforming with `ExpressibleByDictionaryLiteral`
5098+ // protocol, duplicated keys may be allowed.
5099+ if (!(type && type->isDictionary ())) {
5100+ return {true , E};
5101+ }
5102+
5103+ using LiteralKey = std::pair<std::string, ExprKind>;
5104+ using Element = std::pair<const TupleExpr *, size_t >;
5105+
5106+ std::map<LiteralKey, llvm::SmallVector<Element, 4 >> groupedLiteralKeys;
5107+
5108+ for (size_t i = 0 ; i < DLE->getElements ().size (); ++i) {
5109+ const auto *elt = DLE->getElement (i);
5110+ const auto *tupleElt = cast<TupleExpr>(elt);
5111+ const auto *keyExpr =
5112+ tupleElt->getElement (0 )->getSemanticsProvidingExpr ();
5113+ auto *LE = dyn_cast<LiteralExpr>(keyExpr);
5114+ if (!LE)
5115+ continue ;
5116+
5117+ if (!shouldDiagnoseLiteral (LE))
5118+ continue ;
5119+
5120+ auto keyStringValue = getKeyStringValue (LE);
5121+ auto literalKey = std::make_pair (keyStringValue, keyExpr->getKind ());
5122+ groupedLiteralKeys[literalKey].push_back ({tupleElt, i});
5123+ }
5124+
5125+ // All keys are unique.
5126+ if (groupedLiteralKeys.size () == DLE->getNumElements ()) {
5127+ return {true , E};
5128+ }
5129+
5130+ auto &DE = Ctx.Diags ;
5131+ auto emitNoteWithFixit = [&](const Element &duplicated) {
5132+ auto note = DE.diagnose (duplicated.first ->getLoc (),
5133+ diag::duplicated_key_declared_here);
5134+ auto duplicatedEltIdx = duplicated.second ;
5135+ const auto commanLocs = DLE->getCommaLocs ();
5136+ note.fixItRemove (duplicated.first ->getSourceRange ());
5137+ if (duplicatedEltIdx < commanLocs.size ()) {
5138+ note.fixItRemove (commanLocs[duplicatedEltIdx]);
5139+ } else {
5140+ // For the last element remove the previous comma.
5141+ note.fixItRemove (commanLocs[duplicatedEltIdx - 1 ]);
5142+ }
5143+ };
5144+
5145+ for (auto &entry : groupedLiteralKeys) {
5146+ auto &keyValuePairs = entry.second ;
5147+ if (keyValuePairs.size () == 1 ) {
5148+ continue ;
5149+ }
5150+
5151+ auto elt = keyValuePairs.front ();
5152+ const auto keyValue = entry.first .first ;
5153+ const auto keyExpr = cast<LiteralExpr>(
5154+ elt.first ->getElement (0 )->getSemanticsProvidingExpr ());
5155+ const auto value = getKeyStringValueForDiagnostic (keyExpr);
5156+ DE.diagnose (elt.first ->getLoc (),
5157+ diag::duplicated_literal_keys_in_dictionary_literal, type,
5158+ keyExpr->getLiteralKindDescription (), value.empty (), value);
5159+ for (auto &duplicated : keyValuePairs) {
5160+ emitNoteWithFixit (duplicated);
5161+ }
5162+ }
5163+ return {true , E};
5164+ }
5165+ };
5166+
5167+ DiagnoseWalker Walker (DC);
5168+ const_cast <Expr *>(E)->walk (Walker);
5169+ }
5170+
50145171namespace {
50155172
50165173class CompletionHandlerUsageChecker final : public ASTWalker {
@@ -5098,6 +5255,7 @@ void swift::performSyntacticExprDiagnostics(const Expr *E,
50985255 diagDeprecatedObjCSelectors (DC, E);
50995256 diagnoseConstantArgumentRequirement (E, DC);
51005257 diagUnqualifiedAccessToMethodNamedSelf (E, DC);
5258+ diagnoseDictionaryLiteralDuplicateKeyEntries (E, DC);
51015259}
51025260
51035261void swift::performStmtDiagnostics (const Stmt *S, DeclContext *DC) {
0 commit comments