diff options
Diffstat (limited to 'clang/lib/Sema/SemaExprObjC.cpp')
-rw-r--r-- | clang/lib/Sema/SemaExprObjC.cpp | 236 |
1 files changed, 189 insertions, 47 deletions
diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index c61b13cf5980..228a1ec3ba1f 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -894,6 +894,62 @@ ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) { ArrayWithObjectsMethod, SR)); } +/// Check for duplicate keys in an ObjC dictionary literal. For instance: +/// NSDictionary *nd = @{ @"foo" : @"bar", @"foo" : @"baz" }; +static void +CheckObjCDictionaryLiteralDuplicateKeys(Sema &S, + ObjCDictionaryLiteral *Literal) { + if (Literal->isValueDependent() || Literal->isTypeDependent()) + return; + + // NSNumber has quite relaxed equality semantics (for instance, @YES is + // considered equal to @1.0). For now, ignore floating points and just do a + // bit-width and sign agnostic integer compare. + struct APSIntCompare { + bool operator()(const llvm::APSInt &LHS, const llvm::APSInt &RHS) const { + return llvm::APSInt::compareValues(LHS, RHS) < 0; + } + }; + + llvm::DenseMap<StringRef, SourceLocation> StringKeys; + std::map<llvm::APSInt, SourceLocation, APSIntCompare> IntegralKeys; + + auto checkOneKey = [&](auto &Map, const auto &Key, SourceLocation Loc) { + auto Pair = Map.insert({Key, Loc}); + if (!Pair.second) { + S.Diag(Loc, diag::warn_nsdictionary_duplicate_key); + S.Diag(Pair.first->second, diag::note_nsdictionary_duplicate_key_here); + } + }; + + for (unsigned Idx = 0, End = Literal->getNumElements(); Idx != End; ++Idx) { + Expr *Key = Literal->getKeyValueElement(Idx).Key->IgnoreParenImpCasts(); + + if (auto *StrLit = dyn_cast<ObjCStringLiteral>(Key)) { + StringRef Bytes = StrLit->getString()->getBytes(); + SourceLocation Loc = StrLit->getExprLoc(); + checkOneKey(StringKeys, Bytes, Loc); + } + + if (auto *BE = dyn_cast<ObjCBoxedExpr>(Key)) { + Expr *Boxed = BE->getSubExpr(); + SourceLocation Loc = BE->getExprLoc(); + + // Check for @("foo"). + if (auto *Str = dyn_cast<StringLiteral>(Boxed->IgnoreParenImpCasts())) { + checkOneKey(StringKeys, Str->getBytes(), Loc); + continue; + } + + Expr::EvalResult Result; + if (Boxed->EvaluateAsInt(Result, S.getASTContext(), + Expr::SE_AllowSideEffects)) { + checkOneKey(IntegralKeys, Result.Val.getInt(), Loc); + } + } + } +} + ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR, MutableArrayRef<ObjCDictionaryElement> Elements) { SourceLocation Loc = SR.getBegin(); @@ -1061,12 +1117,14 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR, HasPackExpansions = true; } - QualType Ty - = Context.getObjCObjectPointerType( - Context.getObjCInterfaceType(NSDictionaryDecl)); - return MaybeBindToTemporary(ObjCDictionaryLiteral::Create( - Context, Elements, HasPackExpansions, Ty, - DictionaryWithObjectsMethod, SR)); + QualType Ty = Context.getObjCObjectPointerType( + Context.getObjCInterfaceType(NSDictionaryDecl)); + + auto *Literal = + ObjCDictionaryLiteral::Create(Context, Elements, HasPackExpansions, Ty, + DictionaryWithObjectsMethod, SR); + CheckObjCDictionaryLiteralDuplicateKeys(*this, Literal); + return MaybeBindToTemporary(Literal); } ExprResult Sema::BuildObjCEncodeExpression(SourceLocation AtLoc, @@ -1170,33 +1228,66 @@ static void DiagnoseMismatchedSelectors(Sema &S, SourceLocation AtLoc, } } -static void HelperToDiagnoseDirectSelectorsExpr(Sema &S, SourceLocation AtLoc, - Selector Sel, - ObjCMethodList &MethList, - bool &onlyDirect) { +static ObjCMethodDecl *LookupDirectMethodInMethodList(Sema &S, Selector Sel, + ObjCMethodList &MethList, + bool &onlyDirect, + bool &anyDirect) { + (void)Sel; ObjCMethodList *M = &MethList; - for (M = M->getNext(); M; M = M->getNext()) { + ObjCMethodDecl *DirectMethod = nullptr; + for (; M; M = M->getNext()) { ObjCMethodDecl *Method = M->getMethod(); - if (Method->getSelector() != Sel) + if (!Method) continue; - if (!Method->isDirectMethod()) + assert(Method->getSelector() == Sel && "Method with wrong selector in method list"); + if (Method->isDirectMethod()) { + anyDirect = true; + DirectMethod = Method; + } else onlyDirect = false; } + + return DirectMethod; } -static void DiagnoseDirectSelectorsExpr(Sema &S, SourceLocation AtLoc, - Selector Sel, bool &onlyDirect) { - for (Sema::GlobalMethodPool::iterator b = S.MethodPool.begin(), - e = S.MethodPool.end(); b != e; b++) { - // first, instance methods - ObjCMethodList &InstMethList = b->second.first; - HelperToDiagnoseDirectSelectorsExpr(S, AtLoc, Sel, InstMethList, - onlyDirect); +// Search the global pool for (potentially) direct methods matching the given +// selector. If a non-direct method is found, set \param onlyDirect to false. If +// a direct method is found, set \param anyDirect to true. Returns a direct +// method, if any. +static ObjCMethodDecl *LookupDirectMethodInGlobalPool(Sema &S, Selector Sel, + bool &onlyDirect, + bool &anyDirect) { + auto Iter = S.MethodPool.find(Sel); + if (Iter == S.MethodPool.end()) + return nullptr; - // second, class methods - ObjCMethodList &ClsMethList = b->second.second; - HelperToDiagnoseDirectSelectorsExpr(S, AtLoc, Sel, ClsMethList, onlyDirect); - } + ObjCMethodDecl *DirectInstance = LookupDirectMethodInMethodList( + S, Sel, Iter->second.first, onlyDirect, anyDirect); + ObjCMethodDecl *DirectClass = LookupDirectMethodInMethodList( + S, Sel, Iter->second.second, onlyDirect, anyDirect); + + return DirectInstance ? DirectInstance : DirectClass; +} + +static ObjCMethodDecl *findMethodInCurrentClass(Sema &S, Selector Sel) { + auto *CurMD = S.getCurMethodDecl(); + if (!CurMD) + return nullptr; + ObjCInterfaceDecl *IFace = CurMD->getClassInterface(); + + // The language enforce that only one direct method is present in a given + // class, so we just need to find one method in the current class to know + // whether Sel is potentially direct in this context. + if (ObjCMethodDecl *MD = IFace->lookupMethod(Sel, /*isInstance=*/true)) + return MD; + if (ObjCMethodDecl *MD = IFace->lookupPrivateMethod(Sel, /*isInstance=*/true)) + return MD; + if (ObjCMethodDecl *MD = IFace->lookupMethod(Sel, /*isInstance=*/false)) + return MD; + if (ObjCMethodDecl *MD = IFace->lookupPrivateMethod(Sel, /*isInstance=*/false)) + return MD; + + return nullptr; } ExprResult Sema::ParseObjCSelectorExpression(Selector Sel, @@ -1222,15 +1313,38 @@ ExprResult Sema::ParseObjCSelectorExpression(Selector Sel, } else Diag(SelLoc, diag::warn_undeclared_selector) << Sel; } else { - bool onlyDirect = Method->isDirectMethod(); - DiagnoseDirectSelectorsExpr(*this, AtLoc, Sel, onlyDirect); DiagnoseMismatchedSelectors(*this, AtLoc, Method, LParenLoc, RParenLoc, WarnMultipleSelectors); + + bool onlyDirect = true; + bool anyDirect = false; + ObjCMethodDecl *GlobalDirectMethod = + LookupDirectMethodInGlobalPool(*this, Sel, onlyDirect, anyDirect); + if (onlyDirect) { Diag(AtLoc, diag::err_direct_selector_expression) << Method->getSelector(); Diag(Method->getLocation(), diag::note_direct_method_declared_at) << Method->getDeclName(); + } else if (anyDirect) { + // If we saw any direct methods, see if we see a direct member of the + // current class. If so, the @selector will likely be used to refer to + // this direct method. + ObjCMethodDecl *LikelyTargetMethod = findMethodInCurrentClass(*this, Sel); + if (LikelyTargetMethod && LikelyTargetMethod->isDirectMethod()) { + Diag(AtLoc, diag::warn_potentially_direct_selector_expression) << Sel; + Diag(LikelyTargetMethod->getLocation(), + diag::note_direct_method_declared_at) + << LikelyTargetMethod->getDeclName(); + } else if (!LikelyTargetMethod) { + // Otherwise, emit the "strict" variant of this diagnostic, unless + // LikelyTargetMethod is non-direct. + Diag(AtLoc, diag::warn_strict_potentially_direct_selector_expression) + << Sel; + Diag(GlobalDirectMethod->getLocation(), + diag::note_direct_method_declared_at) + << GlobalDirectMethod->getDeclName(); + } } } @@ -1953,7 +2067,8 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, if (const ObjCPropertyDecl *PDecl = Setter->findPropertyDecl()) { // Do not warn if user is using property-dot syntax to make call to // user named setter. - if (!(PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_setter)) + if (!(PDecl->getPropertyAttributes() & + ObjCPropertyAttribute::kind_setter)) Diag(MemberLoc, diag::warn_property_access_suggest) << MemberName << QualType(OPT, 0) << PDecl->getName() @@ -2570,6 +2685,16 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, diag::err_illegal_message_expr_incomplete_type)) return ExprError(); + if (Method && Method->isDirectMethod() && SuperLoc.isValid()) { + Diag(SuperLoc, diag::err_messaging_super_with_direct_method) + << FixItHint::CreateReplacement( + SuperLoc, getLangOpts().ObjCAutoRefCount + ? "self" + : Method->getClassInterface()->getName()); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + // Warn about explicit call of +initialize on its own class. But not on 'super'. if (Method && Method->getMethodFamily() == OMF_initialize) { if (!SuperLoc.isValid()) { @@ -2774,9 +2899,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, ReceiverType->isIntegerType())) { // Implicitly convert integers and pointers to 'id' but emit a warning. // But not in ARC. - Diag(Loc, diag::warn_bad_receiver_type) - << ReceiverType - << Receiver->getSourceRange(); + Diag(Loc, diag::warn_bad_receiver_type) << ReceiverType << RecRange; if (ReceiverType->isPointerType()) { Receiver = ImpCastExprToType(Receiver, Context.getObjCIdType(), CK_CPointerToObjCPointerCast).get(); @@ -2927,11 +3050,10 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, // definition is found in a module that's not visible. const ObjCInterfaceDecl *forwardClass = nullptr; if (RequireCompleteType(Loc, OCIType->getPointeeType(), - getLangOpts().ObjCAutoRefCount - ? diag::err_arc_receiver_forward_instance - : diag::warn_receiver_forward_instance, - Receiver? Receiver->getSourceRange() - : SourceRange(SuperLoc))) { + getLangOpts().ObjCAutoRefCount + ? diag::err_arc_receiver_forward_instance + : diag::warn_receiver_forward_instance, + RecRange)) { if (getLangOpts().ObjCAutoRefCount) return ExprError(); @@ -2993,8 +3115,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, return ExprError(); } else { // Reject other random receiver types (e.g. structs). - Diag(Loc, diag::err_bad_receiver_type) - << ReceiverType << Receiver->getSourceRange(); + Diag(Loc, diag::err_bad_receiver_type) << ReceiverType << RecRange; return ExprError(); } } @@ -3012,15 +3133,35 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, << Method->getDeclName(); } - if (ReceiverType->isObjCClassType() && !isImplicit) { - Diag(Receiver->getExprLoc(), - diag::err_messaging_class_with_direct_method); + // Under ARC, self can't be assigned, and doing a direct call to `self` + // when it's a Class is hence safe. For other cases, we can't trust `self` + // is what we think it is, so we reject it. + if (ReceiverType->isObjCClassType() && !isImplicit && + !(Receiver->isObjCSelfExpr() && getLangOpts().ObjCAutoRefCount)) { + { + DiagnosticBuilder Builder = + Diag(Receiver->getExprLoc(), + diag::err_messaging_class_with_direct_method); + if (Receiver->isObjCSelfExpr()) { + Builder.AddFixItHint(FixItHint::CreateReplacement( + RecRange, Method->getClassInterface()->getName())); + } + } Diag(Method->getLocation(), diag::note_direct_method_declared_at) << Method->getDeclName(); } if (SuperLoc.isValid()) { - Diag(SuperLoc, diag::err_messaging_super_with_direct_method); + { + DiagnosticBuilder Builder = + Diag(SuperLoc, diag::err_messaging_super_with_direct_method); + if (ReceiverType->isObjCClassType()) { + Builder.AddFixItHint(FixItHint::CreateReplacement( + SuperLoc, Method->getClassInterface()->getName())); + } else { + Builder.AddFixItHint(FixItHint::CreateReplacement(SuperLoc, "self")); + } + } Diag(Method->getLocation(), diag::note_direct_method_declared_at) << Method->getDeclName(); } @@ -3232,7 +3373,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, if (!isImplicit && Method) { if (const ObjCPropertyDecl *Prop = Method->findPropertyDecl()) { bool IsWeak = - Prop->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_weak; + Prop->getPropertyAttributes() & ObjCPropertyAttribute::kind_weak; if (!IsWeak && Sel.isUnarySelector()) IsWeak = ReturnType.getObjCLifetime() & Qualifiers::OCL_Weak; if (IsWeak && !isUnevaluatedContext() && @@ -4337,7 +4478,7 @@ Sema::CheckObjCConversion(SourceRange castRange, QualType castType, // to 'NSString *', instead of falling through to report a "bridge cast" // diagnostic. if (castACTC == ACTC_retainable && exprACTC == ACTC_none && - ConversionToObjCStringLiteralCheck(castType, castExpr, Diagnose)) + CheckConversionToObjCLiteral(castType, castExpr, Diagnose)) return ACR_error; // Do not issue "bridge cast" diagnostic when implicit casting @@ -4400,9 +4541,10 @@ Expr *Sema::stripARCUnbridgedCast(Expr *e) { } else if (UnaryOperator *uo = dyn_cast<UnaryOperator>(e)) { assert(uo->getOpcode() == UO_Extension); Expr *sub = stripARCUnbridgedCast(uo->getSubExpr()); - return new (Context) - UnaryOperator(sub, UO_Extension, sub->getType(), sub->getValueKind(), - sub->getObjectKind(), uo->getOperatorLoc(), false); + return UnaryOperator::Create(Context, sub, UO_Extension, sub->getType(), + sub->getValueKind(), sub->getObjectKind(), + uo->getOperatorLoc(), false, + CurFPFeatureOverrides()); } else if (GenericSelectionExpr *gse = dyn_cast<GenericSelectionExpr>(e)) { assert(!gse->isResultDependent()); |