aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Sema/SemaExprObjC.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Sema/SemaExprObjC.cpp')
-rw-r--r--clang/lib/Sema/SemaExprObjC.cpp236
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());