• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
2
3// The larger set of tests in located in localization.m. These are tests
4// specific for non-aggressive reporting.
5
6// These declarations were reduced using Delta-Debugging from Foundation.h
7// on Mac OS X.
8
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
38
39@interface LocalizationTestSuite : NSObject
40int random();
41@property (assign) int unreadArticlesCount;
42@end
43#define MCLocalizedString(s) NSLocalizedString(s,nil);
44// Test cases begin here
45@implementation LocalizationTestSuite
46
47NSString *KHLocalizedString(NSString* key, NSString* comment) {
48    return NSLocalizedString(key, comment);
49}
50
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];
55
56  [testLabel setText:[argumentObject text]]; // no-warning
57  [testLabel setText:argumentObject.text];   // no-warning
58}
59
60- (void)testLocalizationErrorDetectedOnPathway {
61  UILabel *testLabel = [[UILabel alloc] init];
62  NSString *bar = NSLocalizedString(@"Hello", @"Comment");
63
64  if (random()) {
65    bar = @"Unlocalized string";
66  }
67
68  [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}}
69}
70
71- (void)testOneCharacterStringsDoNotGiveAWarning {
72  UILabel *testLabel = [[UILabel alloc] init];
73  NSString *bar = NSLocalizedString(@"Hello", @"Comment");
74
75  if (random()) {
76    bar = @"-";
77  }
78
79  [testLabel setText:bar]; // no-warning
80}
81
82- (void)testOneCharacterUTFStringsDoNotGiveAWarning {
83  UILabel *testLabel = [[UILabel alloc] init];
84  NSString *bar = NSLocalizedString(@"Hello", @"Comment");
85
86  if (random()) {
87    bar = @"\u2014";
88  }
89
90  [testLabel setText:bar]; // no-warning
91}
92
93// Plural Misuse Checker Tests
94// These tests are modeled off incorrect uses of the many-one pattern
95// from real projects.
96
97- (NSString *)test1:(int)plural {
98    if (plural) {
99        return MCLocalizedString(@"TYPE_PLURAL"); // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
100    }
101    return MCLocalizedString(@"TYPE");
102}
103
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}}
107    }
108    return nil;
109}
110
111- (void)test3 {
112    NSString *count;
113    if (self.unreadArticlesCount > 1)
114    {
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}}
116    } else {
117        count = [count stringByAppendingFormat:@"%@",  KHLocalizedString(@"New Story", @"One new story")]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
118    }
119}
120
121- (NSString *)test4:(int)count {
122    if ( count == 1 )
123    {
124        return [NSString stringWithFormat:KHLocalizedString(@"value.singular",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
125    } else {
126        return [NSString stringWithFormat:KHLocalizedString(@"value.plural",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
127    }
128}
129
130- (NSString *)test5:(int)count {
131	int test = count == 1;
132    if (test)
133    {
134        return [NSString stringWithFormat:KHLocalizedString(@"value.singular",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
135    } else {
136        return [NSString stringWithFormat:KHLocalizedString(@"value.plural",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
137    }
138}
139
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)
141
142- (NSString *)test6:(int)sectionIndex {
143	int someOtherVariable = 0;
144    if (sectionIndex == 1)
145    {
146		// Do some other crazy stuff
147		if (someOtherVariable)
148        	return KHLocalizedString(@"OK",nil); // no-warning
149    } else {
150        return KHLocalizedString(@"value.plural",nil); // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}}
151    }
152	return nil;
153}
154
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
159
160// - (NSString *)testNotAccountedFor:(int)sectionIndex {
161//     if (sectionIndex == 1)
162//     {
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
168// 	}
169// }
170
171// Potential test-cases to support in the future
172
173// - (NSString *)test7:(int)count {
174//     BOOL plural = count != 1;
175//     return KHLocalizedString(plural ? @"PluralString" : @"SingularString", @"");
176// }
177//
178// - (NSString *)test8:(BOOL)plural {
179//     return KHLocalizedString(([NSString stringWithFormat:@"RELATIVE_DATE_%@_%@", ((1 == 1) ? @"FUTURE" : @"PAST"), plural ? @"PLURAL" : @"SINGULAR"]));
180// }
181//
182//
183//
184// - (void)test9:(int)numberOfTimesEarned {
185//     NSString* localizedDescriptionKey;
186//     if (numberOfTimesEarned == 1) {
187//         localizedDescriptionKey = @"SINGULAR_%@";
188//     } else {
189//         localizedDescriptionKey = @"PLURAL_%@_%@";
190//     }
191//     NSLocalizedString(localizedDescriptionKey, nil);
192// }
193//
194// - (NSString *)test10 {
195//     NSInteger count = self.problems.count;
196//     NSString *title = [NSString stringWithFormat:@"%ld Problems", (long) count];
197//     if (count < 2) {
198//         if (count == 0) {
199//             title = [NSString stringWithFormat:@"No Problems Found"];
200//         } else {
201//             title = [NSString stringWithFormat:@"%ld Problem", (long) count];
202//         }
203//     }
204//     return title;
205// }
206
207@end
208