From bf79cedb1170f7c57a5d945c916dfcfc109bd22e Mon Sep 17 00:00:00 2001 From: Nicola Pero Date: Thu, 2 Jun 2011 18:54:32 +0000 Subject: [PATCH] In gcc/objc/: 2011-06-02 Nicola Pero In gcc/objc/: 2011-06-02 Nicola Pero 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. In gcc/testsuite/: 2011-06-02 Nicola Pero 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. From-SVN: r174575 --- gcc/objc/ChangeLog | 9 ++ gcc/objc/objc-act.c | 150 ++++++++++++++++---- gcc/testsuite/ChangeLog | 10 ++ gcc/testsuite/obj-c++.dg/method-6.mm | 11 +- gcc/testsuite/obj-c++.dg/method-7.mm | 9 +- gcc/testsuite/obj-c++.dg/method-lookup-1.mm | 94 ++++++++++++ gcc/testsuite/objc.dg/method-19.m | 5 +- gcc/testsuite/objc.dg/method-5.m | 6 +- gcc/testsuite/objc.dg/method-lookup-1.m | 94 ++++++++++++ 9 files changed, 345 insertions(+), 43 deletions(-) create mode 100644 gcc/testsuite/obj-c++.dg/method-lookup-1.mm create mode 100644 gcc/testsuite/objc.dg/method-lookup-1.m diff --git a/gcc/objc/ChangeLog b/gcc/objc/ChangeLog index c52fcc3ff98..50c80b5c7cb 100644 --- a/gcc/objc/ChangeLog +++ b/gcc/objc/ChangeLog @@ -1,3 +1,12 @@ +2011-06-02 Nicola Pero + + 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 * objc-act.c (objc_decl_method_attributes): Implement nonnull diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c index be65a534f1b..e7acb7f05b1 100644 --- a/gcc/objc/objc-act.c +++ b/gcc/objc/objc-act.c @@ -5432,15 +5432,21 @@ objc_finish_message_expr (tree receiver, tree sel_name, tree method_params, 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))); @@ -5450,7 +5456,10 @@ objc_finish_message_expr (tree receiver, tree sel_name, tree method_params, 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 @@ -5458,7 +5467,27 @@ objc_finish_message_expr (tree receiver, tree sel_name, tree method_params, 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) { @@ -5474,28 +5503,41 @@ objc_finish_message_expr (tree receiver, tree sel_name, tree method_params, 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 ' or 'Class ', first search - in protocols themselves for the method prototype. */ + /* If messaging 'id ' or 'Class ', 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 ' but did not find a class method - prototype, search for an instance method instead, and warn - about having done so. */ + /* If messaging 'Class ' 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 @@ -5509,6 +5551,8 @@ objc_finish_message_expr (tree receiver, tree sel_name, tree method_params, } 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) @@ -5523,25 +5567,70 @@ objc_finish_message_expr (tree receiver, tree sel_name, tree method_params, 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 *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 *x;" is exactly + equivalent to "id 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); @@ -5566,6 +5655,7 @@ objc_finish_message_expr (tree receiver, tree sel_name, tree method_params, } 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 @@ -5573,11 +5663,13 @@ objc_finish_message_expr (tree receiver, tree sel_name, tree method_params, 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) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 61f9f16ded5..c6c4b068366 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2011-06-02 Nicola Pero + + 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 * lib/timeout.exp (timeout): Add board_info support. diff --git a/gcc/testsuite/obj-c++.dg/method-6.mm b/gcc/testsuite/obj-c++.dg/method-6.mm index 6c21e799589..0e099227cd1 100644 --- a/gcc/testsuite/obj-c++.dg/method-6.mm +++ b/gcc/testsuite/obj-c++.dg/method-6.mm @@ -1,6 +1,5 @@ -/* 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 } */ @@ -9,11 +8,11 @@ @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 } */ - diff --git a/gcc/testsuite/obj-c++.dg/method-7.mm b/gcc/testsuite/obj-c++.dg/method-7.mm index e9e2d3a4307..85abf7d3117 100644 --- a/gcc/testsuite/obj-c++.dg/method-7.mm +++ b/gcc/testsuite/obj-c++.dg/method-7.mm @@ -1,6 +1,5 @@ /* Check if sending messages to "underspecified" objects is handled gracefully. */ /* Author: Ziemowit Laski . */ - /* { dg-do compile } */ @class UnderSpecified; @@ -10,10 +9,12 @@ typedef struct NotAClass { 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 } */ diff --git a/gcc/testsuite/obj-c++.dg/method-lookup-1.mm b/gcc/testsuite/obj-c++.dg/method-lookup-1.mm new file mode 100644 index 00000000000..47499c37c08 --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/method-lookup-1.mm @@ -0,0 +1,94 @@ +/* Contributed by Nicola Pero , 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 y, id w, id z, NotKnown *a, NotKnown *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 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 z" means that "z" responds to any instance + methods in the protocol, but not class methods. To select class + methods, you use "Class 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 *b" means that "a" is an instance of + NotKnown, and also implements protocol . 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. */ +} diff --git a/gcc/testsuite/objc.dg/method-19.m b/gcc/testsuite/objc.dg/method-19.m index 362d55858f9..0e099227cd1 100644 --- a/gcc/testsuite/objc.dg/method-19.m +++ b/gcc/testsuite/objc.dg/method-19.m @@ -8,8 +8,9 @@ @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 } */ diff --git a/gcc/testsuite/objc.dg/method-5.m b/gcc/testsuite/objc.dg/method-5.m index 9fa8cb6d988..85abf7d3117 100644 --- a/gcc/testsuite/objc.dg/method-5.m +++ b/gcc/testsuite/objc.dg/method-5.m @@ -11,8 +11,10 @@ void foo(UnderSpecified *u, NotAClass *n) { [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 } */ diff --git a/gcc/testsuite/objc.dg/method-lookup-1.m b/gcc/testsuite/objc.dg/method-lookup-1.m new file mode 100644 index 00000000000..47499c37c08 --- /dev/null +++ b/gcc/testsuite/objc.dg/method-lookup-1.m @@ -0,0 +1,94 @@ +/* Contributed by Nicola Pero , 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 y, id w, id z, NotKnown *a, NotKnown *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 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 z" means that "z" responds to any instance + methods in the protocol, but not class methods. To select class + methods, you use "Class 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 *b" means that "a" is an instance of + NotKnown, and also implements protocol . 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. */ +} -- 2.30.2