Home | History | Annotate | Download | only in Analysis
      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
     16 typedef CGFLOAT_TYPE CGFloat;
     17 struct CGPoint {
     18   CGFloat x;
     19   CGFloat y;
     20 };
     21 typedef 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
     54 NSString *ForceLocalized(NSString *str)
     55     __attribute__((annotate("returns_localized_nsstring")));
     56 CGPoint CGPointMake(CGFloat x, CGFloat y);
     57 int random();
     58 // This next one is a made up API
     59 NSString *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
     69 NSString *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