[clang][modules] Don't prevent translation of FW_Private includes when explicitly...
[llvm-project.git] / clang / test / Analysis / localization.m
blob65d857cc9a46eaf935de39b509a0b3c4231a79e8
1 // RUN: %clang_analyze_cc1 -fblocks -analyzer-output=text -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
7 // on Mac OS X.
9 #define nil ((id)0)
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)]
18 @interface NSObject
19 + (id)alloc;
20 - (id)init;
21 @end
22 @interface NSString : NSObject
23 - (NSString *)stringByAppendingFormat:(NSString *)format, ...;
24 + (instancetype)stringWithFormat:(NSString *)format, ...;
25 @end
26 @interface NSBundle : NSObject
27 + (NSBundle *)mainBundle;
28 - (NSString *)localizedStringForKey:(NSString *)key
29                               value:(NSString *)value
30                               table:(NSString *)tableName;
31 @end
32 @interface UILabel : NSObject
33 @property(nullable, nonatomic, copy) NSString *text;
34 @end
35 @interface TestObject : NSObject
36 @property(strong) NSString *text;
37 @end
39 @interface LocalizationTestSuite : NSObject
40 int random(void);
41 @property (assign) int unreadArticlesCount;
42 @end
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");
64   if (random()) { // expected-note {{Assuming the condition is true}} expected-note {{Taking true branch}}
65     bar = @"Unlocalized string"; // expected-note {{Non-localized string literal here}}
66   }
68   [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}} expected-note {{User-facing}}
71 - (void)testMultipleUnlocalizedStringsInSamePath {
72   UILabel *testLabel = [[UILabel alloc] init];
73   NSString *bar = @"Unlocalized string"; // no-note
75   bar = @"Unlocalized string"; // expected-note {{Non-localized string literal here}}
77   NSString *other = @"Other unlocalized string."; // no-note
78   (void)other;
80   NSString *same = @"Unlocalized string"; // no-note
81   (void)same;
83   [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}} expected-note {{User-facing}}
86 - (void)testOneCharacterStringsDoNotGiveAWarning {
87   UILabel *testLabel = [[UILabel alloc] init];
88   NSString *bar = NSLocalizedString(@"Hello", @"Comment");
90   if (random()) {
91     bar = @"-";
92   }
94   [testLabel setText:bar]; // no-warning
97 - (void)testOneCharacterUTFStringsDoNotGiveAWarning {
98   UILabel *testLabel = [[UILabel alloc] init];
99   NSString *bar = NSLocalizedString(@"Hello", @"Comment");
101   if (random()) {
102     bar = @"\u2014";
103   }
105   [testLabel setText:bar]; // no-warning
109 // Suppress diagnostic about user-facing string constants when the method name
110 // contains the term "Debug".
111 - (void)debugScreen:(UILabel *)label {
112   label.text = @"Unlocalized";
115 // Plural Misuse Checker Tests
116 // These tests are modeled off incorrect uses of the many-one pattern
117 // from real projects. 
119 - (NSString *)test1:(int)plural {
120     if (plural) {
121         return MCLocalizedString(@"TYPE_PLURAL"); // expected-warning {{Plural cases are not supported across all languages. Use a .stringsdict file}} expected-note {{Plural}}
122     }
123     return MCLocalizedString(@"TYPE");
126 - (NSString *)test2:(int)numOfReminders {
127     if (numOfReminders > 0) {
128         return [NSString stringWithFormat:@"%@, %@", @"Test", (numOfReminders != 1) ? [NSString stringWithFormat:NSLocalizedString(@"%@ Reminders", @"Plural count of reminders"), numOfReminders] : [NSString stringWithFormat:NSLocalizedString(@"1 reminder", @"One reminder")]]; // expected-warning 2 {{Plural cases are not supported across all languages. Use a .stringsdict file}} expected-note 2 {{Plural}}
129     } 
130     return nil;
133 - (void)test3 {
134     NSString *count;
135     if (self.unreadArticlesCount > 1)
136     {
137         count = [count stringByAppendingFormat:@"%@", KHLocalizedString(@"New Stories", @"Plural count for new stories")]; // expected-warning {{Plural cases are not supported across all languages. Use a .stringsdict file}} expected-note {{Plural}}
138     } else {
139         count = [count stringByAppendingFormat:@"%@",  KHLocalizedString(@"New Story", @"One new story")]; // expected-warning {{Plural cases are not supported across all languages. Use a .stringsdict file}} expected-note {{Plural}}
140     }
143 - (NSString *)test4:(int)count {
144     if ( count == 1 )
145     {
146         return [NSString stringWithFormat:KHLocalizedString(@"value.singular",nil), count]; // expected-warning {{Plural cases are not supported across all languages. Use a .stringsdict file}} expected-note {{Plural}}
147     } else {
148         return [NSString stringWithFormat:KHLocalizedString(@"value.plural",nil), count]; // expected-warning {{Plural cases are not supported across all languages. Use a .stringsdict file}} expected-note {{Plural}}
149     }
152 - (NSString *)test5:(int)count {
153         int test = count == 1;
154     if (test)
155     {
156         return [NSString stringWithFormat:KHLocalizedString(@"value.singular",nil), count]; // expected-warning {{Plural cases are not supported across all languages. Use a .stringsdict file}} expected-note {{Plural}}
157     } else {
158         return [NSString stringWithFormat:KHLocalizedString(@"value.plural",nil), count]; // expected-warning {{Plural cases are not supported across all languages. Use a .stringsdict file}} expected-note {{Plural}}
159     }
162 // 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)
164 - (NSString *)test6:(int)sectionIndex {
165         int someOtherVariable = 0;
166     if (sectionIndex == 1)
167     {
168                 // Do some other crazy stuff
169                 if (someOtherVariable)
170                 return KHLocalizedString(@"OK",nil); // no-warning
171     } else {
172         return KHLocalizedString(@"value.plural",nil); // expected-warning {{Plural cases are not supported across all languages. Use a .stringsdict file}} expected-note {{Plural}}
173     }
174         return nil;
177 // False positives that we are not accounting for involve matching the heuristic
178 // of having 1 or 2 in the RHS of a BinaryOperator and having a localized string 
179 // in the body of the IfStmt. This is seen a lot when checking for the section
180 // indexpath of something like a UITableView
182 // - (NSString *)testNotAccountedFor:(int)sectionIndex {
183 //     if (sectionIndex == 1)
184 //     {
185 //         return KHLocalizedString(@"1",nil); // false-positive
186 //     } else if (sectionIndex == 2) {
187 //      return KHLocalizedString(@"2",nil); // false-positive
188 //     } else if (sectionIndex == 3) {
189 //              return KHLocalizedString(@"3",nil); // no-false-positive
190 //      }
191 // }
193 // Potential test-cases to support in the future
195 // - (NSString *)test7:(int)count {
196 //     BOOL plural = count != 1;
197 //     return KHLocalizedString(plural ? @"PluralString" : @"SingularString", @"");
198 // }
200 // - (NSString *)test8:(BOOL)plural {
201 //     return KHLocalizedString(([NSString stringWithFormat:@"RELATIVE_DATE_%@_%@", ((1 == 1) ? @"FUTURE" : @"PAST"), plural ? @"PLURAL" : @"SINGULAR"]));
202 // }
206 // - (void)test9:(int)numberOfTimesEarned {
207 //     NSString* localizedDescriptionKey;
208 //     if (numberOfTimesEarned == 1) {
209 //         localizedDescriptionKey = @"SINGULAR_%@";
210 //     } else {
211 //         localizedDescriptionKey = @"PLURAL_%@_%@";
212 //     }
213 //     NSLocalizedString(localizedDescriptionKey, nil);
214 // }
216 // - (NSString *)test10 {
217 //     NSInteger count = self.problems.count;
218 //     NSString *title = [NSString stringWithFormat:@"%ld Problems", (long) count];
219 //     if (count < 2) {
220 //         if (count == 0) {
221 //             title = [NSString stringWithFormat:@"No Problems Found"];
222 //         } else {
223 //             title = [NSString stringWithFormat:@"%ld Problem", (long) count];
224 //         }
225 //     }
226 //     return title;
227 // }
229 @end
232 // Suppress diagnostic about user-facing string constants when the class name
233 // contains "Debug"
234 @interface MyDebugView : NSObject
235 @end
237 @implementation MyDebugView
238 - (void)setupScreen:(UILabel *)label {
239   label.text = @"Unlocalized"; // no-warning
241 @end