1 // RUN: %clang_cc1 -fblocks -x objective-c-header -emit-pch -o %t.pch %S/Inputs/localization-pch.h
3 // RUN: %clang_analyze_cc1 -fblocks \
4 // RUN: -analyzer-config optin.osx.cocoa.localizability.NonLocalizedStringChecker:AggressiveReport=true \
5 // RUN: -analyzer-checker=optin.osx.cocoa.localizability.NonLocalizedStringChecker \
6 // RUN: -analyzer-checker=optin.osx.cocoa.localizability.EmptyLocalizationContextChecker \
7 // RUN: -include-pch %t.pch -verify %s
9 // These declarations were reduced using Delta-Debugging from Foundation.h
13 #define NSLocalizedString(key, comment) \
14 [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]
15 #define NSLocalizedStringFromTable(key, tbl, comment) \
16 [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)]
17 #define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \
18 [bundle localizedStringForKey:(key) value:@"" table:(tbl)]
19 #define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) \
20 [bundle localizedStringForKey:(key) value:(val) table:(tbl)]
21 #define CGFLOAT_TYPE double
22 typedef CGFLOAT_TYPE CGFloat;
27 typedef struct CGPoint CGPoint;
33 @interface NSString : NSObject
34 - (void)drawAtPoint:(CGPoint)point withAttributes:(NSDictionary *)attrs;
35 + (instancetype)localizedStringWithFormat:(NSString *)format, ...;
37 @interface NSBundle : NSObject
38 + (NSBundle *)mainBundle;
39 - (NSString *)localizedStringForKey:(NSString *)key
40 value:(NSString *)value
41 table:(NSString *)tableName;
43 @protocol UIAccessibility
44 - (void)accessibilitySetIdentification:(NSString *)ident;
45 - (void)setAccessibilityLabel:(NSString *)label;
47 @interface UILabel : NSObject <UIAccessibility>
48 @property(nullable, nonatomic, copy) NSString *text;
50 @interface TestObject : NSObject
51 @property(strong) NSString *text;
53 @interface NSView : NSObject
54 @property (strong) NSString *toolTip;
56 @interface NSViewSubclass : NSView
59 @interface LocalizationTestSuite : NSObject
60 NSString *ForceLocalized(NSString *str)
61 __attribute__((annotate("returns_localized_nsstring")));
62 CGPoint CGPointMake(CGFloat x, CGFloat y);
64 // This next one is a made up API
65 NSString *CFNumberFormatterCreateStringWithNumber(float x);
66 + (NSString *)forceLocalized:(NSString *)str
67 __attribute__((annotate("returns_localized_nsstring")));
68 + (NSString *)takesLocalizedString:
69 (NSString *)__attribute__((annotate("takes_localized_nsstring")))str;
73 takesLocalizedString(NSString *str
74 __attribute__((annotate("takes_localized_nsstring")))) {
78 // Test cases begin here
79 @implementation LocalizationTestSuite
81 // A C-Funtion that returns a localized string because it has the
82 // "returns_localized_nsstring" annotation
83 NSString *ForceLocalized(NSString *str) { return str; }
84 // An ObjC method that returns a localized string because it has the
85 // "returns_localized_nsstring" annotation
86 + (NSString *)forceLocalized:(NSString *)str {
90 + (NSString *) takesLocalizedString:(NSString *)str { return str; }
92 // An ObjC method that returns a localized string
93 + (NSString *)unLocalizedStringMethod {
94 return @"UnlocalizedString";
97 - (void)testLocalizationErrorDetectedOnPathway {
98 UILabel *testLabel = [[UILabel alloc] init];
99 NSString *bar = NSLocalizedString(@"Hello", @"Comment");
102 bar = @"Unlocalized string";
105 [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}}
108 - (void)testLocalizationErrorDetectedOnNSString {
109 NSString *bar = NSLocalizedString(@"Hello", @"Comment");
112 bar = @"Unlocalized string";
115 [bar drawAtPoint:CGPointMake(0, 0) withAttributes:nil]; // expected-warning {{User-facing text should use localized string macro}}
118 - (void)testNoLocalizationErrorDetectedFromCFunction {
119 UILabel *testLabel = [[UILabel alloc] init];
120 NSString *bar = CFNumberFormatterCreateStringWithNumber(1);
122 [testLabel setText:bar]; // no-warning
125 - (void)testAnnotationAddsLocalizedStateForCFunction {
126 UILabel *testLabel = [[UILabel alloc] init];
127 NSString *bar = NSLocalizedString(@"Hello", @"Comment");
130 bar = @"Unlocalized string";
133 [testLabel setText:ForceLocalized(bar)]; // no-warning
136 - (void)testAnnotationAddsLocalizedStateForObjCMethod {
137 UILabel *testLabel = [[UILabel alloc] init];
138 NSString *bar = NSLocalizedString(@"Hello", @"Comment");
141 bar = @"Unlocalized string";
144 [testLabel setText:[LocalizationTestSuite forceLocalized:bar]]; // no-warning
147 // An empty string literal @"" should not raise an error
148 - (void)testEmptyStringLiteralHasLocalizedState {
149 UILabel *testLabel = [[UILabel alloc] init];
152 [testLabel setText:bar]; // no-warning
155 // An empty string literal @"" inline should not raise an error
156 - (void)testInlineEmptyStringLiteralHasLocalizedState {
157 UILabel *testLabel = [[UILabel alloc] init];
158 [testLabel setText:@""]; // no-warning
161 // An string literal @"Hello" inline should raise an error
162 - (void)testInlineStringLiteralHasLocalizedState {
163 UILabel *testLabel = [[UILabel alloc] init];
164 [testLabel setText:@"Hello"]; // expected-warning {{User-facing text should use localized string macro}}
167 // A nil string should not raise an error
168 - (void)testNilStringIsNotMarkedAsUnlocalized {
169 UILabel *testLabel = [[UILabel alloc] init];
170 [testLabel setText:nil]; // no-warning
173 // A method that takes in a localized string and returns a string
174 // most likely that string is localized.
175 - (void)testLocalizedStringArgument {
176 UILabel *testLabel = [[UILabel alloc] init];
177 NSString *localizedString = NSLocalizedString(@"Hello", @"Comment");
179 NSString *combinedString =
180 [NSString localizedStringWithFormat:@"%@", localizedString];
182 [testLabel setText:combinedString]; // no-warning
185 // A String passed in as a an parameter should not be considered
187 - (void)testLocalizedStringAsArgument:(NSString *)argumentString {
188 UILabel *testLabel = [[UILabel alloc] init];
190 [testLabel setText:argumentString]; // no-warning
193 // The warning is expected to be seen in localizedStringAsArgument: body
194 - (void)testLocalizedStringAsArgumentOtherMethod:(NSString *)argumentString {
195 [self localizedStringAsArgument:@"UnlocalizedString"];
198 // A String passed into another method that calls a method that
199 // requires a localized string should give an error
200 - (void)localizedStringAsArgument:(NSString *)argumentString {
201 UILabel *testLabel = [[UILabel alloc] init];
203 [testLabel setText:argumentString]; // expected-warning {{User-facing text should use localized string macro}}
206 // [LocalizationTestSuite unLocalizedStringMethod] returns an unlocalized string
207 // so we expect an error. Unfrtunately, it probably doesn't make a difference
208 // what [LocalizationTestSuite unLocalizedStringMethod] returns since all
209 // string values returned are marked as Unlocalized in aggressive reporting.
210 - (void)testUnLocalizedStringMethod {
211 UILabel *testLabel = [[UILabel alloc] init];
212 NSString *bar = NSLocalizedString(@"Hello", @"Comment");
214 [testLabel setText:[LocalizationTestSuite unLocalizedStringMethod]]; // expected-warning {{User-facing text should use localized string macro}}
217 // This is the reverse situation: accessibilitySetIdentification: doesn't care
218 // about localization so we don't expect a warning
219 - (void)testMethodNotInRequiresLocalizedStringMethods {
220 UILabel *testLabel = [[UILabel alloc] init];
222 [testLabel accessibilitySetIdentification:@"UnlocalizedString"]; // no-warning
225 // An NSView subclass should raise a warning for methods in NSView that
226 // require localized strings
227 - (void)testRequiresLocalizationMethodFromSuperclass {
228 NSViewSubclass *s = [[NSViewSubclass alloc] init];
229 NSString *bar = @"UnlocalizedString";
231 [s setToolTip:bar]; // expected-warning {{User-facing text should use localized string macro}}
234 - (void)testRequiresLocalizationMethodFromProtocol {
235 UILabel *testLabel = [[UILabel alloc] init];
237 [testLabel setAccessibilityLabel:@"UnlocalizedString"]; // expected-warning {{User-facing text should use localized string macro}}
240 // EmptyLocalizationContextChecker tests
241 #define HOM(s) YOLOC(s)
242 #define YOLOC(x) NSLocalizedString(x, nil)
244 - (void)testNilLocalizationContext {
245 NSString *string = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
246 NSString *string2 = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
247 NSString *string3 = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
250 - (void)testEmptyLocalizationContext {
251 NSString *string = NSLocalizedString(@"LocalizedString", @""); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
252 NSString *string2 = NSLocalizedString(@"LocalizedString", @" "); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
253 NSString *string3 = NSLocalizedString(@"LocalizedString", @" "); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
256 - (void)testNSLocalizedStringVariants {
257 NSString *string = NSLocalizedStringFromTable(@"LocalizedString", nil, @""); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
258 NSString *string2 = NSLocalizedStringFromTableInBundle(@"LocalizedString", nil, [[NSBundle alloc] init],@""); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
259 NSString *string3 = NSLocalizedStringWithDefaultValue(@"LocalizedString", nil, [[NSBundle alloc] init], nil,@""); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
262 - (void)testMacroExpansionNilString {
263 NSString *string = YOLOC(@"Hello"); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
264 NSString *string2 = HOM(@"Hello"); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
265 NSString *string3 = NSLocalizedString((0 ? @"Critical" : @"Current"),nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
268 - (void)testMacroExpansionDefinedInPCH {
269 NSString *string = MyLocalizedStringInPCH(@"Hello"); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
272 #define KCLocalizedString(x,comment) NSLocalizedString(x, comment)
273 #define POSSIBLE_FALSE_POSITIVE(s,other) KCLocalizedString(s,@"Comment")
275 - (void)testNoWarningForNilCommentPassedIntoOtherMacro {
276 NSString *string = KCLocalizedString(@"Hello",@""); // no-warning
277 NSString *string2 = KCLocalizedString(@"Hello",nil); // no-warning
278 NSString *string3 = KCLocalizedString(@"Hello",@"Comment"); // no-warning
281 - (void)testPossibleFalsePositiveSituationAbove {
282 NSString *string = POSSIBLE_FALSE_POSITIVE(@"Hello", nil); // no-warning
283 NSString *string2 = POSSIBLE_FALSE_POSITIVE(@"Hello", @"Hello"); // no-warning
286 - (void)testTakesLocalizedString {
287 NSString *localized = NSLocalizedString(@"Hello", @"World");
288 NSString *alsoLocalized = [LocalizationTestSuite takesLocalizedString:localized]; // no-warning
289 NSString *stillLocalized = [LocalizationTestSuite takesLocalizedString:alsoLocalized]; // no-warning
290 takesLocalizedString(stillLocalized); // no-warning
292 [LocalizationTestSuite takesLocalizedString:@"not localized"]; // expected-warning {{User-facing text should use localized string macro}}
293 takesLocalizedString(@"not localized"); // expected-warning {{User-facing text should use localized string macro}}
297 @interface SynthesizedAccessors : NSObject
298 @property (assign) NSObject *obj;
301 @implementation SynthesizedAccessors