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