Disallow using ObjC literals in direct comparisons (== and friends).
Objective-C literals conceptually always create new objects, but may be
optimized by the compiler or runtime (constant folding, singletons, etc).
Comparing addresses of these objects is relying on this optimization
behavior, which is really an implementation detail.
In the case of == and !=, offer a fixit to a call to -isEqual:, if the
method is available. This fixit is directly on the error so that it is
automatically applied.
Most of the time, this is really a newbie mistake, hence the fixit.
llvm-svn: 158230
diff --git a/clang/test/SemaObjC/objc-literal-comparison.m b/clang/test/SemaObjC/objc-literal-comparison.m
new file mode 100644
index 0000000..23cb42b
--- /dev/null
+++ b/clang/test/SemaObjC/objc-literal-comparison.m
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+typedef unsigned char BOOL;
+
+@interface BaseObject
++ (instancetype)new;
+@end
+
+@interface NSObject : BaseObject
+- (BOOL)isEqual:(id)other;
+@end
+
+@interface NSNumber : NSObject
++ (NSNumber *)numberWithInt:(int)value;
++ (NSNumber *)numberWithDouble:(double)value;
++ (NSNumber *)numberWithBool:(BOOL)value;
+@end
+
+@interface NSArray : NSObject
++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt;
+@end
+
+@interface NSDictionary : NSObject
++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt;
+@end
+
+@interface NSString : NSObject
+@end
+
+void testComparisonsWithFixits(id obj) {
+ if (obj == @"") return; // expected-error{{direct comparison of a string literal is not allowed; use -isEqual: instead}}
+ if (obj != @"") return; // expected-error{{direct comparison of a string literal is not allowed; use -isEqual: instead}}
+ if (@"" == obj) return; // expected-error{{direct comparison of a string literal is not allowed; use -isEqual: instead}}
+ if (@"" == @"") return; // expected-error{{direct comparison of a string literal is not allowed; use -isEqual: instead}}
+
+ if (@[] == obj) return; // expected-error{{direct comparison of an array literal is not allowed; use -isEqual: instead}}
+ if (@{} == obj) return; // expected-error{{direct comparison of a dictionary literal is not allowed; use -isEqual: instead}}
+ if (@12 == obj) return; // expected-error{{direct comparison of a numeric literal is not allowed; use -isEqual: instead}}
+ if (@1.0 == obj) return; // expected-error{{direct comparison of a numeric literal is not allowed; use -isEqual: instead}}
+ if (@__objc_yes == obj) return; // expected-error{{direct comparison of a numeric literal is not allowed; use -isEqual: instead}}
+ if (@(1+1) == obj) return; // expected-error{{direct comparison of a boxed expression is not allowed; use -isEqual: instead}}
+}
+
+
+@interface BadEqualReturnString : NSString
+- (void)isEqual:(id)other;
+@end
+
+@interface BadEqualArgString : NSString
+- (BOOL)isEqual:(int)other;
+@end
+
+
+void testComparisonsWithoutFixits() {
+ // All of these verifications use regex form to ensure we /don't/ append
+ // "use -isEqual: instead" to any of these.
+
+ if ([BaseObject new] == @"") return; // expected-error-re{{direct comparison of a string literal is not allowed$}}
+
+ if ([BadEqualReturnString new] == @"") return; // expected-error-re{{direct comparison of a string literal is not allowed$}}
+ if ([BadEqualArgString new] == @"") return; // expected-error-re{{direct comparison of a string literal is not allowed$}}
+
+ if (@"" < @"") return; // expected-error-re{{direct comparison of a string literal is not allowed$}}
+ if (@"" > @"") return; // expected-error-re{{direct comparison of a string literal is not allowed$}}
+ if (@"" <= @"") return; // expected-error-re{{direct comparison of a string literal is not allowed$}}
+ if (@"" >= @"") return; // expected-error-re{{direct comparison of a string literal is not allowed$}}
+}
+