1// RUN: %clang_cc1 -fblocks -x objective-c-header -emit-pch -o %t.pch %S/Inputs/localization-pch.h 2 3// RUN: %clang_analyze_cc1 -fblocks -analyzer-store=region \ 4// RUN: -analyzer-config optin.osx.cocoa.localizability.NonLocalizedStringChecker:AggressiveReport=true \ 5// RUN: -analyzer-checker=optin.osx.cocoa.localizability.NonLocalizedStringChecker \ 6// RUN: -analyzer-checker=optin.osx.cocoa.localizability.EmptyLocalizationContextChecker \ 7// RUN: -include-pch %t.pch -verify %s 8 9// These declarations were reduced using Delta-Debugging from Foundation.h 10// on Mac OS X. 11 12#define nil ((id)0) 13#define NSLocalizedString(key, comment) \ 14 [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil] 15#define NSLocalizedStringFromTable(key, tbl, comment) \ 16 [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)] 17#define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \ 18 [bundle localizedStringForKey:(key) value:@"" table:(tbl)] 19#define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) \ 20 [bundle localizedStringForKey:(key) value:(val) table:(tbl)] 21#define CGFLOAT_TYPE double 22typedef CGFLOAT_TYPE CGFloat; 23struct CGPoint { 24 CGFloat x; 25 CGFloat y; 26}; 27typedef struct CGPoint CGPoint; 28@interface NSObject 29+ (id)alloc; 30- (id)init; 31@end 32@class NSDictionary; 33@interface NSString : NSObject 34- (void)drawAtPoint:(CGPoint)point withAttributes:(NSDictionary *)attrs; 35+ (instancetype)localizedStringWithFormat:(NSString *)format, ...; 36@end 37@interface NSBundle : NSObject 38+ (NSBundle *)mainBundle; 39- (NSString *)localizedStringForKey:(NSString *)key 40 value:(NSString *)value 41 table:(NSString *)tableName; 42@end 43@protocol UIAccessibility 44- (void)accessibilitySetIdentification:(NSString *)ident; 45- (void)setAccessibilityLabel:(NSString *)label; 46@end 47@interface UILabel : NSObject <UIAccessibility> 48@property(nullable, nonatomic, copy) NSString *text; 49@end 50@interface TestObject : NSObject 51@property(strong) NSString *text; 52@end 53@interface NSView : NSObject 54@property (strong) NSString *toolTip; 55@end 56@interface NSViewSubclass : NSView 57@end 58 59@interface LocalizationTestSuite : NSObject 60NSString *ForceLocalized(NSString *str) 61 __attribute__((annotate("returns_localized_nsstring"))); 62CGPoint CGPointMake(CGFloat x, CGFloat y); 63int random(); 64// This next one is a made up API 65NSString *CFNumberFormatterCreateStringWithNumber(float x); 66+ (NSString *)forceLocalized:(NSString *)str 67 __attribute__((annotate("returns_localized_nsstring"))); 68+ (NSString *)takesLocalizedString: 69 (NSString *)__attribute__((annotate("takes_localized_nsstring")))str; 70@end 71 72NSString * 73takesLocalizedString(NSString *str 74 __attribute__((annotate("takes_localized_nsstring")))) { 75 return str; 76} 77 78// Test cases begin here 79@implementation LocalizationTestSuite 80 81// A C-Funtion that returns a localized string because it has the 82// "returns_localized_nsstring" annotation 83NSString *ForceLocalized(NSString *str) { return str; } 84// An ObjC method that returns a localized string because it has the 85// "returns_localized_nsstring" annotation 86+ (NSString *)forceLocalized:(NSString *)str { 87 return str; 88} 89 90+ (NSString *) takesLocalizedString:(NSString *)str { return str; } 91 92// An ObjC method that returns a localized string 93+ (NSString *)unLocalizedStringMethod { 94 return @"UnlocalizedString"; 95} 96 97- (void)testLocalizationErrorDetectedOnPathway { 98 UILabel *testLabel = [[UILabel alloc] init]; 99 NSString *bar = NSLocalizedString(@"Hello", @"Comment"); 100 101 if (random()) { 102 bar = @"Unlocalized string"; 103 } 104 105 [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}} 106} 107 108- (void)testLocalizationErrorDetectedOnNSString { 109 NSString *bar = NSLocalizedString(@"Hello", @"Comment"); 110 111 if (random()) { 112 bar = @"Unlocalized string"; 113 } 114 115 [bar drawAtPoint:CGPointMake(0, 0) withAttributes:nil]; // expected-warning {{User-facing text should use localized string macro}} 116} 117 118- (void)testNoLocalizationErrorDetectedFromCFunction { 119 UILabel *testLabel = [[UILabel alloc] init]; 120 NSString *bar = CFNumberFormatterCreateStringWithNumber(1); 121 122 [testLabel setText:bar]; // no-warning 123} 124 125- (void)testAnnotationAddsLocalizedStateForCFunction { 126 UILabel *testLabel = [[UILabel alloc] init]; 127 NSString *bar = NSLocalizedString(@"Hello", @"Comment"); 128 129 if (random()) { 130 bar = @"Unlocalized string"; 131 } 132 133 [testLabel setText:ForceLocalized(bar)]; // no-warning 134} 135 136- (void)testAnnotationAddsLocalizedStateForObjCMethod { 137 UILabel *testLabel = [[UILabel alloc] init]; 138 NSString *bar = NSLocalizedString(@"Hello", @"Comment"); 139 140 if (random()) { 141 bar = @"Unlocalized string"; 142 } 143 144 [testLabel setText:[LocalizationTestSuite forceLocalized:bar]]; // no-warning 145} 146 147// An empty string literal @"" should not raise an error 148- (void)testEmptyStringLiteralHasLocalizedState { 149 UILabel *testLabel = [[UILabel alloc] init]; 150 NSString *bar = @""; 151 152 [testLabel setText:bar]; // no-warning 153} 154 155// An empty string literal @"" inline should not raise an error 156- (void)testInlineEmptyStringLiteralHasLocalizedState { 157 UILabel *testLabel = [[UILabel alloc] init]; 158 [testLabel setText:@""]; // no-warning 159} 160 161// An string literal @"Hello" inline should raise an error 162- (void)testInlineStringLiteralHasLocalizedState { 163 UILabel *testLabel = [[UILabel alloc] init]; 164 [testLabel setText:@"Hello"]; // expected-warning {{User-facing text should use localized string macro}} 165} 166 167// A nil string should not raise an error 168- (void)testNilStringIsNotMarkedAsUnlocalized { 169 UILabel *testLabel = [[UILabel alloc] init]; 170 [testLabel setText:nil]; // no-warning 171} 172 173// A method that takes in a localized string and returns a string 174// most likely that string is localized. 175- (void)testLocalizedStringArgument { 176 UILabel *testLabel = [[UILabel alloc] init]; 177 NSString *localizedString = NSLocalizedString(@"Hello", @"Comment"); 178 179 NSString *combinedString = 180 [NSString localizedStringWithFormat:@"%@", localizedString]; 181 182 [testLabel setText:combinedString]; // no-warning 183} 184 185// A String passed in as a an parameter should not be considered 186// unlocalized 187- (void)testLocalizedStringAsArgument:(NSString *)argumentString { 188 UILabel *testLabel = [[UILabel alloc] init]; 189 190 [testLabel setText:argumentString]; // no-warning 191} 192 193// The warning is expected to be seen in localizedStringAsArgument: body 194- (void)testLocalizedStringAsArgumentOtherMethod:(NSString *)argumentString { 195 [self localizedStringAsArgument:@"UnlocalizedString"]; 196} 197 198// A String passed into another method that calls a method that 199// requires a localized string should give an error 200- (void)localizedStringAsArgument:(NSString *)argumentString { 201 UILabel *testLabel = [[UILabel alloc] init]; 202 203 [testLabel setText:argumentString]; // expected-warning {{User-facing text should use localized string macro}} 204} 205 206// [LocalizationTestSuite unLocalizedStringMethod] returns an unlocalized string 207// so we expect an error. Unfrtunately, it probably doesn't make a difference 208// what [LocalizationTestSuite unLocalizedStringMethod] returns since all 209// string values returned are marked as Unlocalized in aggressive reporting. 210- (void)testUnLocalizedStringMethod { 211 UILabel *testLabel = [[UILabel alloc] init]; 212 NSString *bar = NSLocalizedString(@"Hello", @"Comment"); 213 214 [testLabel setText:[LocalizationTestSuite unLocalizedStringMethod]]; // expected-warning {{User-facing text should use localized string macro}} 215} 216 217// This is the reverse situation: accessibilitySetIdentification: doesn't care 218// about localization so we don't expect a warning 219- (void)testMethodNotInRequiresLocalizedStringMethods { 220 UILabel *testLabel = [[UILabel alloc] init]; 221 222 [testLabel accessibilitySetIdentification:@"UnlocalizedString"]; // no-warning 223} 224 225// An NSView subclass should raise a warning for methods in NSView that 226// require localized strings 227- (void)testRequiresLocalizationMethodFromSuperclass { 228 NSViewSubclass *s = [[NSViewSubclass alloc] init]; 229 NSString *bar = @"UnlocalizedString"; 230 231 [s setToolTip:bar]; // expected-warning {{User-facing text should use localized string macro}} 232} 233 234- (void)testRequiresLocalizationMethodFromProtocol { 235 UILabel *testLabel = [[UILabel alloc] init]; 236 237 [testLabel setAccessibilityLabel:@"UnlocalizedString"]; // expected-warning {{User-facing text should use localized string macro}} 238} 239 240// EmptyLocalizationContextChecker tests 241#define HOM(s) YOLOC(s) 242#define YOLOC(x) NSLocalizedString(x, nil) 243 244- (void)testNilLocalizationContext { 245 NSString *string = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 246 NSString *string2 = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 247 NSString *string3 = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 248} 249 250- (void)testEmptyLocalizationContext { 251 NSString *string = NSLocalizedString(@"LocalizedString", @""); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 252 NSString *string2 = NSLocalizedString(@"LocalizedString", @" "); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 253 NSString *string3 = NSLocalizedString(@"LocalizedString", @" "); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 254} 255 256- (void)testNSLocalizedStringVariants { 257 NSString *string = NSLocalizedStringFromTable(@"LocalizedString", nil, @""); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 258 NSString *string2 = NSLocalizedStringFromTableInBundle(@"LocalizedString", nil, [[NSBundle alloc] init],@""); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 259 NSString *string3 = NSLocalizedStringWithDefaultValue(@"LocalizedString", nil, [[NSBundle alloc] init], nil,@""); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 260} 261 262- (void)testMacroExpansionNilString { 263 NSString *string = YOLOC(@"Hello"); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 264 NSString *string2 = HOM(@"Hello"); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 265 NSString *string3 = NSLocalizedString((0 ? @"Critical" : @"Current"),nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 266} 267 268- (void)testMacroExpansionDefinedInPCH { 269 NSString *string = MyLocalizedStringInPCH(@"Hello"); // expected-warning {{Localized string macro should include a non-empty comment for translators}} 270} 271 272#define KCLocalizedString(x,comment) NSLocalizedString(x, comment) 273#define POSSIBLE_FALSE_POSITIVE(s,other) KCLocalizedString(s,@"Comment") 274 275- (void)testNoWarningForNilCommentPassedIntoOtherMacro { 276 NSString *string = KCLocalizedString(@"Hello",@""); // no-warning 277 NSString *string2 = KCLocalizedString(@"Hello",nil); // no-warning 278 NSString *string3 = KCLocalizedString(@"Hello",@"Comment"); // no-warning 279} 280 281- (void)testPossibleFalsePositiveSituationAbove { 282 NSString *string = POSSIBLE_FALSE_POSITIVE(@"Hello", nil); // no-warning 283 NSString *string2 = POSSIBLE_FALSE_POSITIVE(@"Hello", @"Hello"); // no-warning 284} 285 286- (void)testTakesLocalizedString { 287 NSString *localized = NSLocalizedString(@"Hello", @"World"); 288 NSString *alsoLocalized = [LocalizationTestSuite takesLocalizedString:localized]; // no-warning 289 NSString *stillLocalized = [LocalizationTestSuite takesLocalizedString:alsoLocalized]; // no-warning 290 takesLocalizedString(stillLocalized); // no-warning 291 292 [LocalizationTestSuite takesLocalizedString:@"not localized"]; // expected-warning {{User-facing text should use localized string macro}} 293 takesLocalizedString(@"not localized"); // expected-warning {{User-facing text should use localized string macro}} 294} 295@end 296 297@interface SynthesizedAccessors : NSObject 298@property (assign) NSObject *obj; 299@end 300 301@implementation SynthesizedAccessors 302// no-crash 303@end 304