|  | // RUN: %clang_cc1 -fblocks -x objective-c-header -emit-pch -o %t.pch %S/Inputs/localization-pch.h | 
|  |  | 
|  | // RUN: %clang_analyze_cc1 -fblocks -analyzer-store=region  -analyzer-checker=optin.osx.cocoa.localizability.NonLocalizedStringChecker -analyzer-checker=optin.osx.cocoa.localizability.EmptyLocalizationContextChecker -include-pch %t.pch -verify  -analyzer-config AggressiveReport=true %s | 
|  |  | 
|  | // These declarations were reduced using Delta-Debugging from Foundation.h | 
|  | // on Mac OS X. | 
|  |  | 
|  | #define nil ((id)0) | 
|  | #define NSLocalizedString(key, comment)                                        \ | 
|  | [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil] | 
|  | #define NSLocalizedStringFromTable(key, tbl, comment)                          \ | 
|  | [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)] | 
|  | #define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment)          \ | 
|  | [bundle localizedStringForKey:(key) value:@"" table:(tbl)] | 
|  | #define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment)      \ | 
|  | [bundle localizedStringForKey:(key) value:(val) table:(tbl)] | 
|  | #define CGFLOAT_TYPE double | 
|  | typedef CGFLOAT_TYPE CGFloat; | 
|  | struct CGPoint { | 
|  | CGFloat x; | 
|  | CGFloat y; | 
|  | }; | 
|  | typedef struct CGPoint CGPoint; | 
|  | @interface NSObject | 
|  | + (id)alloc; | 
|  | - (id)init; | 
|  | @end | 
|  | @class NSDictionary; | 
|  | @interface NSString : NSObject | 
|  | - (void)drawAtPoint:(CGPoint)point withAttributes:(NSDictionary *)attrs; | 
|  | + (instancetype)localizedStringWithFormat:(NSString *)format, ...; | 
|  | @end | 
|  | @interface NSBundle : NSObject | 
|  | + (NSBundle *)mainBundle; | 
|  | - (NSString *)localizedStringForKey:(NSString *)key | 
|  | value:(NSString *)value | 
|  | table:(NSString *)tableName; | 
|  | @end | 
|  | @protocol UIAccessibility | 
|  | - (void)accessibilitySetIdentification:(NSString *)ident; | 
|  | - (void)setAccessibilityLabel:(NSString *)label; | 
|  | @end | 
|  | @interface UILabel : NSObject <UIAccessibility> | 
|  | @property(nullable, nonatomic, copy) NSString *text; | 
|  | @end | 
|  | @interface TestObject : NSObject | 
|  | @property(strong) NSString *text; | 
|  | @end | 
|  | @interface NSView : NSObject | 
|  | @property (strong) NSString *toolTip; | 
|  | @end | 
|  | @interface NSViewSubclass : NSView | 
|  | @end | 
|  |  | 
|  | @interface LocalizationTestSuite : NSObject | 
|  | NSString *ForceLocalized(NSString *str) | 
|  | __attribute__((annotate("returns_localized_nsstring"))); | 
|  | CGPoint CGPointMake(CGFloat x, CGFloat y); | 
|  | int random(); | 
|  | // This next one is a made up API | 
|  | NSString *CFNumberFormatterCreateStringWithNumber(float x); | 
|  | + (NSString *)forceLocalized:(NSString *)str | 
|  | __attribute__((annotate("returns_localized_nsstring"))); | 
|  | + (NSString *)takesLocalizedString: | 
|  | (NSString *)__attribute__((annotate("takes_localized_nsstring")))str; | 
|  | @end | 
|  |  | 
|  | NSString * | 
|  | takesLocalizedString(NSString *str | 
|  | __attribute__((annotate("takes_localized_nsstring")))) { | 
|  | return str; | 
|  | } | 
|  |  | 
|  | // Test cases begin here | 
|  | @implementation LocalizationTestSuite | 
|  |  | 
|  | // A C-Funtion that returns a localized string because it has the | 
|  | // "returns_localized_nsstring" annotation | 
|  | NSString *ForceLocalized(NSString *str) { return str; } | 
|  | // An ObjC method that returns a localized string because it has the | 
|  | // "returns_localized_nsstring" annotation | 
|  | + (NSString *)forceLocalized:(NSString *)str { | 
|  | return str; | 
|  | } | 
|  |  | 
|  | + (NSString *) takesLocalizedString:(NSString *)str { return str; } | 
|  |  | 
|  | // An ObjC method that returns a localized string | 
|  | + (NSString *)unLocalizedStringMethod { | 
|  | return @"UnlocalizedString"; | 
|  | } | 
|  |  | 
|  | - (void)testLocalizationErrorDetectedOnPathway { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  | NSString *bar = NSLocalizedString(@"Hello", @"Comment"); | 
|  |  | 
|  | if (random()) { | 
|  | bar = @"Unlocalized string"; | 
|  | } | 
|  |  | 
|  | [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}} | 
|  | } | 
|  |  | 
|  | - (void)testLocalizationErrorDetectedOnNSString { | 
|  | NSString *bar = NSLocalizedString(@"Hello", @"Comment"); | 
|  |  | 
|  | if (random()) { | 
|  | bar = @"Unlocalized string"; | 
|  | } | 
|  |  | 
|  | [bar drawAtPoint:CGPointMake(0, 0) withAttributes:nil]; // expected-warning {{User-facing text should use localized string macro}} | 
|  | } | 
|  |  | 
|  | - (void)testNoLocalizationErrorDetectedFromCFunction { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  | NSString *bar = CFNumberFormatterCreateStringWithNumber(1); | 
|  |  | 
|  | [testLabel setText:bar]; // no-warning | 
|  | } | 
|  |  | 
|  | - (void)testAnnotationAddsLocalizedStateForCFunction { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  | NSString *bar = NSLocalizedString(@"Hello", @"Comment"); | 
|  |  | 
|  | if (random()) { | 
|  | bar = @"Unlocalized string"; | 
|  | } | 
|  |  | 
|  | [testLabel setText:ForceLocalized(bar)]; // no-warning | 
|  | } | 
|  |  | 
|  | - (void)testAnnotationAddsLocalizedStateForObjCMethod { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  | NSString *bar = NSLocalizedString(@"Hello", @"Comment"); | 
|  |  | 
|  | if (random()) { | 
|  | bar = @"Unlocalized string"; | 
|  | } | 
|  |  | 
|  | [testLabel setText:[LocalizationTestSuite forceLocalized:bar]]; // no-warning | 
|  | } | 
|  |  | 
|  | // An empty string literal @"" should not raise an error | 
|  | - (void)testEmptyStringLiteralHasLocalizedState { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  | NSString *bar = @""; | 
|  |  | 
|  | [testLabel setText:bar]; // no-warning | 
|  | } | 
|  |  | 
|  | // An empty string literal @"" inline should not raise an error | 
|  | - (void)testInlineEmptyStringLiteralHasLocalizedState { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  | [testLabel setText:@""]; // no-warning | 
|  | } | 
|  |  | 
|  | // An string literal @"Hello" inline should raise an error | 
|  | - (void)testInlineStringLiteralHasLocalizedState { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  | [testLabel setText:@"Hello"]; // expected-warning {{User-facing text should use localized string macro}} | 
|  | } | 
|  |  | 
|  | // A nil string should not raise an error | 
|  | - (void)testNilStringIsNotMarkedAsUnlocalized { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  | [testLabel setText:nil]; // no-warning | 
|  | } | 
|  |  | 
|  | // A method that takes in a localized string and returns a string | 
|  | // most likely that string is localized. | 
|  | - (void)testLocalizedStringArgument { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  | NSString *localizedString = NSLocalizedString(@"Hello", @"Comment"); | 
|  |  | 
|  | NSString *combinedString = | 
|  | [NSString localizedStringWithFormat:@"%@", localizedString]; | 
|  |  | 
|  | [testLabel setText:combinedString]; // no-warning | 
|  | } | 
|  |  | 
|  | // A String passed in as a an parameter should not be considered | 
|  | // unlocalized | 
|  | - (void)testLocalizedStringAsArgument:(NSString *)argumentString { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  |  | 
|  | [testLabel setText:argumentString]; // no-warning | 
|  | } | 
|  |  | 
|  | // The warning is expected to be seen in localizedStringAsArgument: body | 
|  | - (void)testLocalizedStringAsArgumentOtherMethod:(NSString *)argumentString { | 
|  | [self localizedStringAsArgument:@"UnlocalizedString"]; | 
|  | } | 
|  |  | 
|  | // A String passed into another method that calls a method that | 
|  | // requires a localized string should give an error | 
|  | - (void)localizedStringAsArgument:(NSString *)argumentString { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  |  | 
|  | [testLabel setText:argumentString]; // expected-warning {{User-facing text should use localized string macro}} | 
|  | } | 
|  |  | 
|  | // [LocalizationTestSuite unLocalizedStringMethod] returns an unlocalized string | 
|  | // so we expect an error. Unfrtunately, it probably doesn't make a difference | 
|  | // what [LocalizationTestSuite unLocalizedStringMethod] returns since all | 
|  | // string values returned are marked as Unlocalized in aggressive reporting. | 
|  | - (void)testUnLocalizedStringMethod { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  | NSString *bar = NSLocalizedString(@"Hello", @"Comment"); | 
|  |  | 
|  | [testLabel setText:[LocalizationTestSuite unLocalizedStringMethod]]; // expected-warning {{User-facing text should use localized string macro}} | 
|  | } | 
|  |  | 
|  | // This is the reverse situation: accessibilitySetIdentification: doesn't care | 
|  | // about localization so we don't expect a warning | 
|  | - (void)testMethodNotInRequiresLocalizedStringMethods { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  |  | 
|  | [testLabel accessibilitySetIdentification:@"UnlocalizedString"]; // no-warning | 
|  | } | 
|  |  | 
|  | // An NSView subclass should raise a warning for methods in NSView that | 
|  | // require localized strings | 
|  | - (void)testRequiresLocalizationMethodFromSuperclass { | 
|  | NSViewSubclass *s = [[NSViewSubclass alloc] init]; | 
|  | NSString *bar = @"UnlocalizedString"; | 
|  |  | 
|  | [s setToolTip:bar]; // expected-warning {{User-facing text should use localized string macro}} | 
|  | } | 
|  |  | 
|  | - (void)testRequiresLocalizationMethodFromProtocol { | 
|  | UILabel *testLabel = [[UILabel alloc] init]; | 
|  |  | 
|  | [testLabel setAccessibilityLabel:@"UnlocalizedString"]; // expected-warning {{User-facing text should use localized string macro}} | 
|  | } | 
|  |  | 
|  | // EmptyLocalizationContextChecker tests | 
|  | #define HOM(s) YOLOC(s) | 
|  | #define YOLOC(x) NSLocalizedString(x, nil) | 
|  |  | 
|  | - (void)testNilLocalizationContext { | 
|  | NSString *string = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | NSString *string2 = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | NSString *string3 = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | } | 
|  |  | 
|  | - (void)testEmptyLocalizationContext { | 
|  | NSString *string = NSLocalizedString(@"LocalizedString", @""); // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | NSString *string2 = NSLocalizedString(@"LocalizedString", @" "); // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | NSString *string3 = NSLocalizedString(@"LocalizedString", @"	 "); // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | } | 
|  |  | 
|  | - (void)testNSLocalizedStringVariants { | 
|  | NSString *string = NSLocalizedStringFromTable(@"LocalizedString", nil, @""); // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | NSString *string2 = NSLocalizedStringFromTableInBundle(@"LocalizedString", nil, [[NSBundle alloc] init],@""); // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | NSString *string3 = NSLocalizedStringWithDefaultValue(@"LocalizedString", nil, [[NSBundle alloc] init], nil,@""); // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | } | 
|  |  | 
|  | - (void)testMacroExpansionNilString { | 
|  | NSString *string = YOLOC(@"Hello"); // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | NSString *string2 = HOM(@"Hello");  // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | NSString *string3 = NSLocalizedString((0 ? @"Critical" : @"Current"),nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | } | 
|  |  | 
|  | - (void)testMacroExpansionDefinedInPCH { | 
|  | NSString *string = MyLocalizedStringInPCH(@"Hello"); // expected-warning {{Localized string macro should include a non-empty comment for translators}} | 
|  | } | 
|  |  | 
|  | #define KCLocalizedString(x,comment) NSLocalizedString(x, comment) | 
|  | #define POSSIBLE_FALSE_POSITIVE(s,other) KCLocalizedString(s,@"Comment") | 
|  |  | 
|  | - (void)testNoWarningForNilCommentPassedIntoOtherMacro { | 
|  | NSString *string = KCLocalizedString(@"Hello",@""); // no-warning | 
|  | NSString *string2 = KCLocalizedString(@"Hello",nil); // no-warning | 
|  | NSString *string3 = KCLocalizedString(@"Hello",@"Comment"); // no-warning | 
|  | } | 
|  |  | 
|  | - (void)testPossibleFalsePositiveSituationAbove { | 
|  | NSString *string = POSSIBLE_FALSE_POSITIVE(@"Hello", nil); // no-warning | 
|  | NSString *string2 = POSSIBLE_FALSE_POSITIVE(@"Hello", @"Hello"); // no-warning | 
|  | } | 
|  |  | 
|  | - (void)testTakesLocalizedString { | 
|  | NSString *localized = NSLocalizedString(@"Hello", @"World"); | 
|  | NSString *alsoLocalized = [LocalizationTestSuite takesLocalizedString:localized]; // no-warning | 
|  | NSString *stillLocalized = [LocalizationTestSuite takesLocalizedString:alsoLocalized]; // no-warning | 
|  | takesLocalizedString(stillLocalized); // no-warning | 
|  |  | 
|  | [LocalizationTestSuite takesLocalizedString:@"not localized"]; // expected-warning {{User-facing text should use localized string macro}} | 
|  | takesLocalizedString(@"not localized"); // expected-warning {{User-facing text should use localized string macro}} | 
|  | } | 
|  | @end |