+2011-06-02 Nicola Pero <nicola.pero@meta-innovation.com>
+
+ PR objc/48539
+ * objc-act.c (objc_finish_message_expr): Warn if messaging a class
+ that was only declared using @class without an @interface. Warn
+ if messaging an instance of a class that was only declared using
+ @class without an @interface, unless the receiver was also typed
+ with a protocol list.
+
2011-06-01 Nicola Pero <nicola.pero@meta-innovation.com>
* objc-act.c (objc_decl_method_attributes): Implement nonnull
from the implementation context). */
rtype = receiver;
while (TREE_CODE (rtype) == COMPOUND_EXPR
- || TREE_CODE (rtype) == MODIFY_EXPR
- || CONVERT_EXPR_P (rtype)
- || TREE_CODE (rtype) == COMPONENT_REF)
+ || TREE_CODE (rtype) == MODIFY_EXPR
+ || CONVERT_EXPR_P (rtype)
+ || TREE_CODE (rtype) == COMPONENT_REF)
rtype = TREE_OPERAND (rtype, 0);
+ /* self is 1 if this is a message to self, 0 otherwise */
self = (rtype == self_decl);
+
+ /* super is 1 if this is a message to super, 0 otherwise. */
super = (rtype == UOBJC_SUPER_decl);
+
+ /* rtype is the type of the receiver. */
rtype = TREE_TYPE (receiver);
+ /* have_cast is 1 if the receiver is casted. */
have_cast = (TREE_CODE (receiver) == NOP_EXPR
|| (TREE_CODE (receiver) == COMPOUND_EXPR
&& !IS_SUPER (rtype)));
should_call_super_dealloc = 0;
/* If the receiver is a class object, retrieve the corresponding
- @interface, if one exists. */
+ @interface, if one exists. class_tree is the class name
+ identifier, or NULL_TREE if this is not a class method or the
+ class name could not be determined (as in the case "Class c; [c
+ method];"). */
class_tree = receiver_is_class_object (receiver, self, super);
/* Now determine the receiver type (if an explicit cast has not been
if (!have_cast)
{
if (class_tree)
- rtype = lookup_interface (class_tree);
+ {
+ /* We are here when we have no cast, and we have a class
+ name. So, this is a plain method to a class object, as
+ in [NSObject alloc]. Find the interface corresponding to
+ the class name. */
+ rtype = lookup_interface (class_tree);
+
+ if (rtype == NULL_TREE)
+ {
+ /* If 'rtype' is NULL_TREE at this point it means that
+ we have seen no @interface corresponding to that
+ class name, only a @class declaration. So, we have a
+ class name (class_tree) but no actual details of the
+ class methods. We won't be able to check that the
+ class responds to the method, and we will have to
+ guess the method prototype. Emit a warning, then
+ keep going (this will use any method with a matching
+ name, as if the receiver was of type 'Class'). */
+ warning (0, "@interface of class %qE not found", class_tree);
+ }
+ }
/* Handle `self' and `super'. */
else if (super)
{
rtype = lookup_interface (CLASS_NAME (implementation_template));
}
- /* If receiver is of type `id' or `Class' (or if the @interface for a
- class is not visible), we shall be satisfied with the existence of
- any instance or class method. */
if (objc_is_id (rtype))
{
+ /* The receiver is of type 'id' or 'Class' (with or without some
+ protocols attached to it). */
+
+ /* We set class_tree to the identifier for 'Class' if this is a
+ class method, and to NULL_TREE if not. */
class_tree = (IS_CLASS (rtype) ? objc_class_name : NULL_TREE);
+
+ /* 'rprotos' is the list of protocols that the receiver
+ supports. */
rprotos = (TYPE_HAS_OBJC_INFO (TREE_TYPE (rtype))
? TYPE_OBJC_PROTOCOL_LIST (TREE_TYPE (rtype))
: NULL_TREE);
+
+ /* We have no information on the type, and we set it to
+ NULL_TREE. */
rtype = NULL_TREE;
+ /* If there are any protocols, check that the method we are
+ calling appears in the protocol list. If there are no
+ protocols, this is a message to 'id' or 'Class' and we accept
+ any method that exists. */
if (rprotos)
{
- /* If messaging 'id <Protos>' or 'Class <Proto>', first search
- in protocols themselves for the method prototype. */
+ /* If messaging 'id <Protos>' or 'Class <Proto>', first
+ search in protocols themselves for the method
+ prototype. */
method_prototype
= lookup_method_in_protocol_list (rprotos, sel_name,
class_tree != NULL_TREE);
- /* If messaging 'Class <Proto>' but did not find a class method
- prototype, search for an instance method instead, and warn
- about having done so. */
+ /* If messaging 'Class <Proto>' but did not find a class
+ method prototype, search for an instance method instead,
+ and warn about having done so. */
if (!method_prototype && !rtype && class_tree != NULL_TREE)
{
method_prototype
}
else if (rtype)
{
+ /* We have a receiver type which is more specific than 'id' or
+ 'Class'. */
tree orig_rtype = rtype;
if (TREE_CODE (rtype) == POINTER_TYPE)
rprotos = TYPE_OBJC_PROTOCOL_LIST (rtype);
rtype = TYPE_OBJC_INTERFACE (rtype);
}
- /* If we could not find an @interface declaration, we must have
- only seen a @class declaration; so, we cannot say anything
- more intelligent about which methods the receiver will
- understand. */
if (!rtype || TREE_CODE (rtype) == IDENTIFIER_NODE)
{
+ /* If we could not find an @interface declaration, we must
+ have only seen a @class declaration; so, we cannot say
+ anything more intelligent about which methods the
+ receiver will understand. Note that this only happens
+ for instance methods; for class methods to a class where
+ we have only seen a @class declaration,
+ lookup_interface() above would have set rtype to
+ NULL_TREE. */
+ if (rprotos)
+ {
+ /* We could not find an @interface declaration, yet, if
+ there are protocols attached to the type, we can
+ still look up the method in the protocols. Ie, we
+ are in the following case:
+
+ @class MyClass;
+ MyClass<MyProtocol> *x;
+ [x method];
+
+ If 'MyProtocol' has the method 'method', we can check
+ and retrieve the method prototype. */
+ method_prototype
+ = lookup_method_in_protocol_list (rprotos, sel_name, 0);
+
+ /* At this point, if we have found the method_prototype,
+ we are quite happy. The details of the class are
+ irrelevant. If we haven't found it, a warning will
+ have been produced that the method could not be found
+ in the protocol, and we won't produce further
+ warnings (please note that this means that "@class
+ MyClass; MyClass <MyProtocol> *x;" is exactly
+ equivalent to "id <MyProtocol> x", which isn't too
+ satisfactory but it's not easy to see how to do
+ better). */
+ }
+ else
+ {
+ if (rtype)
+ {
+ /* We could not find an @interface declaration, and
+ there are no protocols attached to the receiver,
+ so we can't complete the check that the receiver
+ responds to the method, and we can't retrieve the
+ method prototype. But, because the receiver has
+ a well-specified class, the programmer did want
+ this check to be performed. Emit a warning, then
+ keep going as if it was an 'id'. To remove the
+ warning, either include an @interface for the
+ class, or cast the receiver to 'id'. Note that
+ rtype is an IDENTIFIER_NODE at this point. */
+ warning (0, "@interface of class %qE not found", rtype);
+ }
+ }
+
rtype = NULL_TREE;
- /* We could not find an @interface declaration, yet Message maybe in a
- @class's protocol. */
- if (!method_prototype && rprotos)
- method_prototype
- = lookup_method_in_protocol_list (rprotos, sel_name, 0);
}
else if (TREE_CODE (rtype) == CLASS_INTERFACE_TYPE
|| TREE_CODE (rtype) == CLASS_IMPLEMENTATION_TYPE)
{
- /* We have a valid ObjC class name. Look up the method name
- in the published @interface for the class (and its
- superclasses). */
+ /* We have a valid ObjC class name with an associated
+ @interface. Look up the method name in the published
+ @interface for the class (and its superclasses). */
method_prototype
= lookup_method_static (rtype, sel_name, class_tree != NULL_TREE);
}
else
{
+ /* We have a type, but it's not an Objective-C type (!). */
warning (0, "invalid receiver type %qs",
identifier_to_locale (gen_type_name (orig_rtype)));
/* After issuing the "invalid receiver" warning, perform method
rtype = rprotos = NULL_TREE;
}
}
+ /* Note that rtype could also be NULL_TREE. This happens if we are
+ messaging a class by name, but the class was only
+ forward-declared using @class. */
-
- /* For 'id' or 'Class' receivers, search in the global hash table
- as a last resort. For all receivers, warn if protocol searches
- have failed. */
+ /* For 'id' or 'Class' receivers, search in the global hash table as
+ a last resort. For all receivers, warn if protocol searches have
+ failed. */
if (!method_prototype)
{
if (rprotos)
+2011-06-02 Nicola Pero <nicola.pero@meta-innovation.com>
+
+ PR objc/48539
+ * objc.dg/method-5.m: Updated.
+ * objc.dg/method-19.m: Updated.
+ * objc.dg/method-lookup-1.m: New.
+ * obj-c++.dg/method-6.mm: Updated.
+ * obj-c++.dg/method-7.mm: Updated.
+ * obj-c++.dg/method-lookup-1.mm: New.
+
2011-06-02 DJ Delorie <dj@redhat.com>
* lib/timeout.exp (timeout): Add board_info support.
-/* The following should NOT generate "may not respond to" warnings,
- since a forward-declared @class (instance) should be treated like a
- 'Class') ('id'). */
+/* The following should NOT generate "may not respond to" warnings, since a forward-declared
+ @class (instance) should be treated like a 'Class') ('id'). */
/* { dg-do compile } */
@class NotKnown;
void foo(NotKnown *n) {
- [NotKnown new];
- [n nonexistent_method]; /* { dg-warning "no .\\-nonexistent_method. method found" } */
+ [NotKnown new]; /* { dg-warning ".interface of class .NotKnown. not found" } */
+ [n nonexistent_method]; /* { dg-warning ".interface of class .NotKnown. not found" } */
+ /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 12 } */
}
/* { dg-warning "Messages without a matching method signature" "" { target *-*-* } 0 } */
/* { dg-warning "will be assumed to return .id. and accept" "" { target *-*-* } 0 } */
/* { dg-warning ".\.\.\.. as arguments" "" { target *-*-* } 0 } */
-
/* Check if sending messages to "underspecified" objects is handled gracefully. */
/* Author: Ziemowit Laski <zlaski@apple.com>. */
-
/* { dg-do compile } */
@class UnderSpecified;
void foo(UnderSpecified *u, NotAClass *n) {
[n nonexistent_method]; /* { dg-warning "invalid receiver type" } */
- /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 12 } */
+ /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 11 } */
[NotAClass nonexistent_method]; /* { dg-error ".NotAClass. is not an Objective\\-C class name or alias" } */
- [u nonexistent_method]; /* { dg-warning "no .\\-nonexistent_method. method found" } */
- [UnderSpecified nonexistent_method]; /* { dg-warning "no .\\+nonexistent_method. method found" } */
+ [u nonexistent_method]; /* { dg-warning ".interface of class .UnderSpecified. not found" } */
+ /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 14 } */
+ [UnderSpecified nonexistent_method]; /* { dg-warning ".interface of class .UnderSpecified. not found" } */
+ /* { dg-warning "no .\\+nonexistent_method. method found" "" { target *-*-* } 16 } */
}
/* { dg-warning "Messages without a matching method signature" "" { target *-*-* } 0 } */
--- /dev/null
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, June 2011. */
+/* { dg-do compile } */
+
+@class NotKnown;
+
+@protocol MyProtocol
++ (id) classMethod;
+- (id) instanceMethod;
+@end
+
+@protocol MyProtocol2
++ (id) classMethod2;
+- (id) instanceMethod2;
+@end
+
+void test (Class x, Class <MyProtocol> y, id w, id <MyProtocol> z, NotKnown *a, NotKnown <MyProtocol> *b)
+{
+ /* "Class x" means that "x" responds to any class methods, and may
+ also respond to instance methods because instance methods of the
+ root class are class methods. */
+ [x classMethod]; /* No warning here. */
+
+ [x instanceMethod]; /* No warning here. */
+
+
+ /* "Class <MyProtocol> y" means that "y" responds to any class
+ methods specified in the protocol MyProtocol, but not to other
+ class or instance methods. If a class method is not found, an
+ instance method from the protocol may be used instead but that is
+ suspicious and gets a warning. */
+ [y classMethod]; /* No warning here. */
+
+ [y instanceMethod]; /* { dg-warning "found .\\-instanceMethod. instead of .\\+instanceMethod. in protocol" } */
+
+ [y classMethod2]; /* { dg-warning ".\\+classMethod2. not found in protocol" } */
+
+ [y instanceMethod2]; /* { dg-warning ".\\+instanceMethod2. not found in protocol" } */
+
+
+ /* If a class is specified by name, the @interface must be available
+ to check what it responds to. */
+ [NotKnown classMethod]; /* { dg-warning ".interface of class .NotKnown. not found" } */
+
+
+ /* "id w" means that "w" responds to anything, both class and
+ instance methods. */
+ [w instanceMethod]; /* No warning here. */
+
+ [w instanceMethod2]; /* No warning here. */
+
+ [w classMethod]; /* No warning here. */
+
+ [w classMethod2]; /* No warning here. */
+
+
+ /* "id <MyProtocol> z" means that "z" responds to any instance
+ methods in the protocol, but not class methods. To select class
+ methods, you use "Class <MyProtocol> z". */
+ [z instanceMethod]; /* No warning here. */
+
+ [z instanceMethod2]; /* { dg-warning ".\\-instanceMethod2. not found in protocol" } */
+
+ [z classMethod]; /* { dg-warning ".\\-classMethod. not found in protocol" } */
+
+ [z classMethod2]; /* { dg-warning ".\\-classMethod2. not found in protocol" } */
+
+
+ /* "NotKnown *a" means that "a" is an instance of NotKnown. Since
+ the programmer explicitly specified the class name, it must be
+ because they expect the compiler to do type-checking; the
+ @interface must be available to do this check, otherwise the
+ compiler does not know what "a" responds to. */
+ [a instanceMethod]; /* { dg-warning ".interface of class .NotKnown. not found" } */
+
+ /* But, if you cast it to "id", then you're disabling type-checking
+ and the warnings should go away. */
+ [(id)a instanceMethod]; /* No warning here. */
+
+
+ /* "NotKnown <MyProtocol> *b" means that "a" is an instance of
+ NotKnown, and also implements protocol <MyProtocol>. If you send
+ a message that is part of the protocol, then the compiler can do
+ type-checking and all is fine. */
+ [b instanceMethod];
+
+ /* But if you send a message that is not part of the protocol, then
+ you'll get a warning that the method can not be found in the
+ protocol. */
+ [b instanceMethod2]; /* { dg-warning ".\\-instanceMethod2. not found in protocol" } */
+
+ /* But, if you cast it to "id", then you're disabling type-checking
+ and the warnings should go away. */
+ [(id)b instanceMethod2]; /* No warning here. */
+}
@class NotKnown;
void foo(NotKnown *n) {
- [NotKnown new];
- [n nonexistent_method]; /* { dg-warning "no .\\-nonexistent_method. method found" } */
+ [NotKnown new]; /* { dg-warning ".interface of class .NotKnown. not found" } */
+ [n nonexistent_method]; /* { dg-warning ".interface of class .NotKnown. not found" } */
+ /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 12 } */
}
/* { dg-warning "Messages without a matching method signature" "" { target *-*-* } 0 } */
[n nonexistent_method]; /* { dg-warning "invalid receiver type" } */
/* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 11 } */
[NotAClass nonexistent_method]; /* { dg-error ".NotAClass. is not an Objective\\-C class name or alias" } */
- [u nonexistent_method]; /* { dg-warning "no .\\-nonexistent_method. method found" } */
- [UnderSpecified nonexistent_method]; /* { dg-warning "no .\\+nonexistent_method. method found" } */
+ [u nonexistent_method]; /* { dg-warning ".interface of class .UnderSpecified. not found" } */
+ /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 14 } */
+ [UnderSpecified nonexistent_method]; /* { dg-warning ".interface of class .UnderSpecified. not found" } */
+ /* { dg-warning "no .\\+nonexistent_method. method found" "" { target *-*-* } 16 } */
}
/* { dg-warning "Messages without a matching method signature" "" { target *-*-* } 0 } */
--- /dev/null
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, June 2011. */
+/* { dg-do compile } */
+
+@class NotKnown;
+
+@protocol MyProtocol
++ (id) classMethod;
+- (id) instanceMethod;
+@end
+
+@protocol MyProtocol2
++ (id) classMethod2;
+- (id) instanceMethod2;
+@end
+
+void test (Class x, Class <MyProtocol> y, id w, id <MyProtocol> z, NotKnown *a, NotKnown <MyProtocol> *b)
+{
+ /* "Class x" means that "x" responds to any class methods, and may
+ also respond to instance methods because instance methods of the
+ root class are class methods. */
+ [x classMethod]; /* No warning here. */
+
+ [x instanceMethod]; /* No warning here. */
+
+
+ /* "Class <MyProtocol> y" means that "y" responds to any class
+ methods specified in the protocol MyProtocol, but not to other
+ class or instance methods. If a class method is not found, an
+ instance method from the protocol may be used instead but that is
+ suspicious and gets a warning. */
+ [y classMethod]; /* No warning here. */
+
+ [y instanceMethod]; /* { dg-warning "found .\\-instanceMethod. instead of .\\+instanceMethod. in protocol" } */
+
+ [y classMethod2]; /* { dg-warning ".\\+classMethod2. not found in protocol" } */
+
+ [y instanceMethod2]; /* { dg-warning ".\\+instanceMethod2. not found in protocol" } */
+
+
+ /* If a class is specified by name, the @interface must be available
+ to check what it responds to. */
+ [NotKnown classMethod]; /* { dg-warning ".interface of class .NotKnown. not found" } */
+
+
+ /* "id w" means that "w" responds to anything, both class and
+ instance methods. */
+ [w instanceMethod]; /* No warning here. */
+
+ [w instanceMethod2]; /* No warning here. */
+
+ [w classMethod]; /* No warning here. */
+
+ [w classMethod2]; /* No warning here. */
+
+
+ /* "id <MyProtocol> z" means that "z" responds to any instance
+ methods in the protocol, but not class methods. To select class
+ methods, you use "Class <MyProtocol> z". */
+ [z instanceMethod]; /* No warning here. */
+
+ [z instanceMethod2]; /* { dg-warning ".\\-instanceMethod2. not found in protocol" } */
+
+ [z classMethod]; /* { dg-warning ".\\-classMethod. not found in protocol" } */
+
+ [z classMethod2]; /* { dg-warning ".\\-classMethod2. not found in protocol" } */
+
+
+ /* "NotKnown *a" means that "a" is an instance of NotKnown. Since
+ the programmer explicitly specified the class name, it must be
+ because they expect the compiler to do type-checking; the
+ @interface must be available to do this check, otherwise the
+ compiler does not know what "a" responds to. */
+ [a instanceMethod]; /* { dg-warning ".interface of class .NotKnown. not found" } */
+
+ /* But, if you cast it to "id", then you're disabling type-checking
+ and the warnings should go away. */
+ [(id)a instanceMethod]; /* No warning here. */
+
+
+ /* "NotKnown <MyProtocol> *b" means that "a" is an instance of
+ NotKnown, and also implements protocol <MyProtocol>. If you send
+ a message that is part of the protocol, then the compiler can do
+ type-checking and all is fine. */
+ [b instanceMethod];
+
+ /* But if you send a message that is not part of the protocol, then
+ you'll get a warning that the method can not be found in the
+ protocol. */
+ [b instanceMethod2]; /* { dg-warning ".\\-instanceMethod2. not found in protocol" } */
+
+ /* But, if you cast it to "id", then you're disabling type-checking
+ and the warnings should go away. */
+ [(id)b instanceMethod2]; /* No warning here. */
+}