@@ -894,6 +894,62 @@ ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) {
894
894
ArrayWithObjectsMethod, SR));
895
895
}
896
896
897
+ // / Check for duplicate keys in an ObjC dictionary literal. For instance:
898
+ // / NSDictionary *nd = @{ @"foo" : @"bar", @"foo" : @"baz" };
899
+ static void
900
+ CheckObjCDictionaryLiteralDuplicateKeys (Sema &S,
901
+ ObjCDictionaryLiteral *Literal) {
902
+ if (Literal->isValueDependent () || Literal->isTypeDependent ())
903
+ return ;
904
+
905
+ // NSNumber has quite relaxed equality semantics (for instance, @YES is
906
+ // considered equal to @1.0). For now, ignore floating points and just do a
907
+ // bit-width and sign agnostic integer compare.
908
+ struct APSIntCompare {
909
+ bool operator ()(const llvm::APSInt &LHS, const llvm::APSInt &RHS) const {
910
+ return llvm::APSInt::compareValues (LHS, RHS) < 0 ;
911
+ }
912
+ };
913
+
914
+ llvm::DenseMap<StringRef, SourceLocation> StringKeys;
915
+ std::map<llvm::APSInt, SourceLocation, APSIntCompare> IntegralKeys;
916
+
917
+ auto checkOneKey = [&](auto &Map, const auto &Key, SourceLocation Loc) {
918
+ auto Pair = Map.insert ({Key, Loc});
919
+ if (!Pair.second ) {
920
+ S.Diag (Loc, diag::warn_nsdictionary_duplicate_key);
921
+ S.Diag (Pair.first ->second , diag::note_nsdictionary_duplicate_key_here);
922
+ }
923
+ };
924
+
925
+ for (unsigned Idx = 0 , End = Literal->getNumElements (); Idx != End; ++Idx) {
926
+ Expr *Key = Literal->getKeyValueElement (Idx).Key ->IgnoreParenImpCasts ();
927
+
928
+ if (auto *StrLit = dyn_cast<ObjCStringLiteral>(Key)) {
929
+ StringRef Bytes = StrLit->getString ()->getBytes ();
930
+ SourceLocation Loc = StrLit->getExprLoc ();
931
+ checkOneKey (StringKeys, Bytes, Loc);
932
+ }
933
+
934
+ if (auto *BE = dyn_cast<ObjCBoxedExpr>(Key)) {
935
+ Expr *Boxed = BE->getSubExpr ();
936
+ SourceLocation Loc = BE->getExprLoc ();
937
+
938
+ // Check for @("foo").
939
+ if (auto *Str = dyn_cast<StringLiteral>(Boxed->IgnoreParenImpCasts ())) {
940
+ checkOneKey (StringKeys, Str->getBytes (), Loc);
941
+ continue ;
942
+ }
943
+
944
+ Expr::EvalResult Result;
945
+ if (Boxed->EvaluateAsInt (Result, S.getASTContext (),
946
+ Expr::SE_AllowSideEffects)) {
947
+ checkOneKey (IntegralKeys, Result.Val .getInt (), Loc);
948
+ }
949
+ }
950
+ }
951
+ }
952
+
897
953
ExprResult Sema::BuildObjCDictionaryLiteral (SourceRange SR,
898
954
MutableArrayRef<ObjCDictionaryElement> Elements) {
899
955
SourceLocation Loc = SR.getBegin ();
@@ -1061,12 +1117,14 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR,
1061
1117
HasPackExpansions = true ;
1062
1118
}
1063
1119
1064
- QualType Ty
1065
- = Context.getObjCObjectPointerType (
1066
- Context.getObjCInterfaceType (NSDictionaryDecl));
1067
- return MaybeBindToTemporary (ObjCDictionaryLiteral::Create (
1068
- Context, Elements, HasPackExpansions, Ty,
1069
- DictionaryWithObjectsMethod, SR));
1120
+ QualType Ty = Context.getObjCObjectPointerType (
1121
+ Context.getObjCInterfaceType (NSDictionaryDecl));
1122
+
1123
+ auto *Literal =
1124
+ ObjCDictionaryLiteral::Create (Context, Elements, HasPackExpansions, Ty,
1125
+ DictionaryWithObjectsMethod, SR);
1126
+ CheckObjCDictionaryLiteralDuplicateKeys (*this , Literal);
1127
+ return MaybeBindToTemporary (Literal);
1070
1128
}
1071
1129
1072
1130
ExprResult Sema::BuildObjCEncodeExpression (SourceLocation AtLoc,
0 commit comments