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