1 // RUN: %clang_cc1 -analyze -fblocks -analyzer-store=region -analyzer-checker=optin.osx.cocoa.localizability.NonLocalizedStringChecker -analyzer-checker=alpha.osx.cocoa.localizability.PluralMisuseChecker -verify %s
3 // The larger set of tests in located in localization.m. These are tests
4 // specific for non-aggressive reporting.
6 // These declarations were reduced using Delta-Debugging from Foundation.h
10 #define NSLocalizedString(key, comment) \
11 [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]
12 #define NSLocalizedStringFromTable(key, tbl, comment) \
13 [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)]
14 #define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \
15 [bundle localizedStringForKey:(key) value:@"" table:(tbl)]
16 #define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) \
17 [bundle localizedStringForKey:(key) value:(val) table:(tbl)]
22 @interface NSString : NSObject
23 - (NSString *)stringByAppendingFormat:(NSString *)format, ...;
24 + (instancetype)stringWithFormat:(NSString *)format, ...;
26 @interface NSBundle : NSObject
27 + (NSBundle *)mainBundle;
28 - (NSString *)localizedStringForKey:(NSString *)key
29 value:(NSString *)value
30 table:(NSString *)tableName;
32 @interface UILabel : NSObject
33 @property(nullable, nonatomic, copy) NSString *text;
35 @interface TestObject : NSObject
36 @property(strong) NSString *text;
39 @interface LocalizationTestSuite : NSObject
41 @property (assign) int unreadArticlesCount;
43 #define MCLocalizedString(s) NSLocalizedString(s,nil);
44 // Test cases begin here
45 @implementation LocalizationTestSuite
47 NSString *KHLocalizedString(NSString* key, NSString* comment) {
48 return NSLocalizedString(key, comment);
51 // An object passed in as an parameter's string member
52 // should not be considered unlocalized
53 - (void)testObjectAsArgument:(TestObject *)argumentObject {
54 UILabel *testLabel = [[UILabel alloc] init];
56 [testLabel setText:[argumentObject text]]; // no-warning
57 [testLabel setText:argumentObject.text]; // no-warning
60 - (void)testLocalizationErrorDetectedOnPathway {
61 UILabel *testLabel = [[UILabel alloc] init];
62 NSString *bar = NSLocalizedString(@"Hello", @"Comment");
65 bar = @"Unlocalized string";
68 [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}}
71 - (void)testOneCharacterStringsDoNotGiveAWarning {
72 UILabel *testLabel = [[UILabel alloc] init];
73 NSString *bar = NSLocalizedString(@"Hello", @"Comment");
79 [testLabel setText:bar]; // no-warning
82 - (void)testOneCharacterUTFStringsDoNotGiveAWarning {
83 UILabel *testLabel = [[UILabel alloc] init];
84 NSString *bar = NSLocalizedString(@"Hello", @"Comment");
90 [testLabel setText:bar]; // no-warning
93 // Plural Misuse Checker Tests
94 // These tests are modeled off incorrect uses of the many-one pattern
95 // from real projects.
97 - (NSString *)test1:(int)plural {
99 return MCLocalizedString(@"TYPE_PLURAL"); // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
101 return MCLocalizedString(@"TYPE");
104 - (NSString *)test2:(int)numOfReminders {
105 if (numOfReminders > 0) {
106 return [NSString stringWithFormat:@"%@, %@", @"Test", (numOfReminders != 1) ? [NSString stringWithFormat:NSLocalizedString(@"%@ Reminders", @"Plural count of reminders"), numOfReminders] : [NSString stringWithFormat:NSLocalizedString(@"1 reminder", @"One reminder")]]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
113 if (self.unreadArticlesCount > 1)
115 count = [count stringByAppendingFormat:@"%@", KHLocalizedString(@"New Stories", @"Plural count for new stories")]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
117 count = [count stringByAppendingFormat:@"%@", KHLocalizedString(@"New Story", @"One new story")]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
121 - (NSString *)test4:(int)count {
124 return [NSString stringWithFormat:KHLocalizedString(@"value.singular",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
126 return [NSString stringWithFormat:KHLocalizedString(@"value.plural",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
130 - (NSString *)test5:(int)count {
131 int test = count == 1;
134 return [NSString stringWithFormat:KHLocalizedString(@"value.singular",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
136 return [NSString stringWithFormat:KHLocalizedString(@"value.plural",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
140 // This tests the heuristic that the direct parent IfStmt must match the isCheckingPlurality confition to avoid false positives generated from complex code (generally the pattern we're looking for is simple If-Else)
142 - (NSString *)test6:(int)sectionIndex {
143 int someOtherVariable = 0;
144 if (sectionIndex == 1)
146 // Do some other crazy stuff
147 if (someOtherVariable)
148 return KHLocalizedString(@"OK",nil); // no-warning
150 return KHLocalizedString(@"value.plural",nil); // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
155 // False positives that we are not accounting for involve matching the heuristic
156 // of having 1 or 2 in the RHS of a BinaryOperator and having a localized string
157 // in the body of the IfStmt. This is seen a lot when checking for the section
158 // indexpath of something like a UITableView
160 // - (NSString *)testNotAccountedFor:(int)sectionIndex {
161 // if (sectionIndex == 1)
163 // return KHLocalizedString(@"1",nil); // false-positive
164 // } else if (sectionIndex == 2) {
165 // return KHLocalizedString(@"2",nil); // false-positive
166 // } else if (sectionIndex == 3) {
167 // return KHLocalizedString(@"3",nil); // no-false-positive
171 // Potential test-cases to support in the future
173 // - (NSString *)test7:(int)count {
174 // BOOL plural = count != 1;
175 // return KHLocalizedString(plural ? @"PluralString" : @"SingularString", @"");
178 // - (NSString *)test8:(BOOL)plural {
179 // return KHLocalizedString(([NSString stringWithFormat:@"RELATIVE_DATE_%@_%@", ((1 == 1) ? @"FUTURE" : @"PAST"), plural ? @"PLURAL" : @"SINGULAR"]));
184 // - (void)test9:(int)numberOfTimesEarned {
185 // NSString* localizedDescriptionKey;
186 // if (numberOfTimesEarned == 1) {
187 // localizedDescriptionKey = @"SINGULAR_%@";
189 // localizedDescriptionKey = @"PLURAL_%@_%@";
191 // NSLocalizedString(localizedDescriptionKey, nil);
194 // - (NSString *)test10 {
195 // NSInteger count = self.problems.count;
196 // NSString *title = [NSString stringWithFormat:@"%ld Problems", (long) count];
199 // title = [NSString stringWithFormat:@"No Problems Found"];
201 // title = [NSString stringWithFormat:@"%ld Problem", (long) count];