@@ -5011,6 +5011,163 @@ static void diagUnqualifiedAccessToMethodNamedSelf(const Expr *E,
5011
5011
const_cast <Expr *>(E)->walk (Walker);
5012
5012
}
5013
5013
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
+
5014
5171
namespace {
5015
5172
5016
5173
class CompletionHandlerUsageChecker final : public ASTWalker {
@@ -5098,6 +5255,7 @@ void swift::performSyntacticExprDiagnostics(const Expr *E,
5098
5255
diagDeprecatedObjCSelectors (DC, E);
5099
5256
diagnoseConstantArgumentRequirement (E, DC);
5100
5257
diagUnqualifiedAccessToMethodNamedSelf (E, DC);
5258
+ diagnoseDictionaryLiteralDuplicateKeyEntries (E, DC);
5101
5259
}
5102
5260
5103
5261
void swift::performStmtDiagnostics (const Stmt *S, DeclContext *DC) {
0 commit comments