forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			304 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Objective-C
		
	
	
	
			
		
		
	
	
			304 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Objective-C
		
	
	
	
// RUN: %clang_cc1 -fblocks -x objective-c-header -emit-pch -o %t.pch %S/Inputs/localization-pch.h
 | 
						|
 | 
						|
// RUN: %clang_analyze_cc1 -fblocks -analyzer-store=region \
 | 
						|
// RUN:   -analyzer-config optin.osx.cocoa.localizability.NonLocalizedStringChecker:AggressiveReport=true \
 | 
						|
// RUN:   -analyzer-checker=optin.osx.cocoa.localizability.NonLocalizedStringChecker \
 | 
						|
// RUN:   -analyzer-checker=optin.osx.cocoa.localizability.EmptyLocalizationContextChecker \
 | 
						|
// RUN:   -include-pch %t.pch -verify  %s
 | 
						|
 | 
						|
// These declarations were reduced using Delta-Debugging from Foundation.h
 | 
						|
// on Mac OS X.
 | 
						|
 | 
						|
#define nil ((id)0)
 | 
						|
#define NSLocalizedString(key, comment)                                        \
 | 
						|
  [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]
 | 
						|
#define NSLocalizedStringFromTable(key, tbl, comment)                          \
 | 
						|
  [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)]
 | 
						|
#define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment)          \
 | 
						|
  [bundle localizedStringForKey:(key) value:@"" table:(tbl)]
 | 
						|
#define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment)      \
 | 
						|
  [bundle localizedStringForKey:(key) value:(val) table:(tbl)]
 | 
						|
#define CGFLOAT_TYPE double
 | 
						|
typedef CGFLOAT_TYPE CGFloat;
 | 
						|
struct CGPoint {
 | 
						|
  CGFloat x;
 | 
						|
  CGFloat y;
 | 
						|
};
 | 
						|
typedef struct CGPoint CGPoint;
 | 
						|
@interface NSObject
 | 
						|
+ (id)alloc;
 | 
						|
- (id)init;
 | 
						|
@end
 | 
						|
@class NSDictionary;
 | 
						|
@interface NSString : NSObject
 | 
						|
- (void)drawAtPoint:(CGPoint)point withAttributes:(NSDictionary *)attrs;
 | 
						|
+ (instancetype)localizedStringWithFormat:(NSString *)format, ...;
 | 
						|
@end
 | 
						|
@interface NSBundle : NSObject
 | 
						|
+ (NSBundle *)mainBundle;
 | 
						|
- (NSString *)localizedStringForKey:(NSString *)key
 | 
						|
                              value:(NSString *)value
 | 
						|
                              table:(NSString *)tableName;
 | 
						|
@end
 | 
						|
@protocol UIAccessibility 
 | 
						|
- (void)accessibilitySetIdentification:(NSString *)ident;
 | 
						|
- (void)setAccessibilityLabel:(NSString *)label;
 | 
						|
@end
 | 
						|
@interface UILabel : NSObject <UIAccessibility>
 | 
						|
@property(nullable, nonatomic, copy) NSString *text;
 | 
						|
@end
 | 
						|
@interface TestObject : NSObject
 | 
						|
@property(strong) NSString *text;
 | 
						|
@end
 | 
						|
@interface NSView : NSObject
 | 
						|
@property (strong) NSString *toolTip;
 | 
						|
@end
 | 
						|
@interface NSViewSubclass : NSView
 | 
						|
@end
 | 
						|
 | 
						|
@interface LocalizationTestSuite : NSObject
 | 
						|
NSString *ForceLocalized(NSString *str)
 | 
						|
    __attribute__((annotate("returns_localized_nsstring")));
 | 
						|
CGPoint CGPointMake(CGFloat x, CGFloat y);
 | 
						|
int random();
 | 
						|
// This next one is a made up API
 | 
						|
NSString *CFNumberFormatterCreateStringWithNumber(float x);
 | 
						|
+ (NSString *)forceLocalized:(NSString *)str
 | 
						|
    __attribute__((annotate("returns_localized_nsstring")));
 | 
						|
+ (NSString *)takesLocalizedString:
 | 
						|
    (NSString *)__attribute__((annotate("takes_localized_nsstring")))str;
 | 
						|
@end
 | 
						|
 | 
						|
NSString *
 | 
						|
takesLocalizedString(NSString *str
 | 
						|
                     __attribute__((annotate("takes_localized_nsstring")))) {
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
// Test cases begin here
 | 
						|
@implementation LocalizationTestSuite
 | 
						|
 | 
						|
// A C-Funtion that returns a localized string because it has the
 | 
						|
// "returns_localized_nsstring" annotation
 | 
						|
NSString *ForceLocalized(NSString *str) { return str; }
 | 
						|
// An ObjC method that returns a localized string because it has the
 | 
						|
// "returns_localized_nsstring" annotation
 | 
						|
+ (NSString *)forceLocalized:(NSString *)str {
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
+ (NSString *) takesLocalizedString:(NSString *)str { return str; }
 | 
						|
 | 
						|
// An ObjC method that returns a localized string
 | 
						|
+ (NSString *)unLocalizedStringMethod {
 | 
						|
  return @"UnlocalizedString";
 | 
						|
}
 | 
						|
 | 
						|
- (void)testLocalizationErrorDetectedOnPathway {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
  NSString *bar = NSLocalizedString(@"Hello", @"Comment");
 | 
						|
 | 
						|
  if (random()) {
 | 
						|
    bar = @"Unlocalized string";
 | 
						|
  }
 | 
						|
 | 
						|
  [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}}
 | 
						|
}
 | 
						|
 | 
						|
- (void)testLocalizationErrorDetectedOnNSString {
 | 
						|
  NSString *bar = NSLocalizedString(@"Hello", @"Comment");
 | 
						|
 | 
						|
  if (random()) {
 | 
						|
    bar = @"Unlocalized string";
 | 
						|
  }
 | 
						|
 | 
						|
  [bar drawAtPoint:CGPointMake(0, 0) withAttributes:nil]; // expected-warning {{User-facing text should use localized string macro}}
 | 
						|
}
 | 
						|
 | 
						|
- (void)testNoLocalizationErrorDetectedFromCFunction {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
  NSString *bar = CFNumberFormatterCreateStringWithNumber(1);
 | 
						|
 | 
						|
  [testLabel setText:bar]; // no-warning
 | 
						|
}
 | 
						|
 | 
						|
- (void)testAnnotationAddsLocalizedStateForCFunction {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
  NSString *bar = NSLocalizedString(@"Hello", @"Comment");
 | 
						|
 | 
						|
  if (random()) {
 | 
						|
    bar = @"Unlocalized string";
 | 
						|
  }
 | 
						|
 | 
						|
  [testLabel setText:ForceLocalized(bar)]; // no-warning
 | 
						|
}
 | 
						|
 | 
						|
- (void)testAnnotationAddsLocalizedStateForObjCMethod {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
  NSString *bar = NSLocalizedString(@"Hello", @"Comment");
 | 
						|
 | 
						|
  if (random()) {
 | 
						|
    bar = @"Unlocalized string";
 | 
						|
  }
 | 
						|
 | 
						|
  [testLabel setText:[LocalizationTestSuite forceLocalized:bar]]; // no-warning
 | 
						|
}
 | 
						|
 | 
						|
// An empty string literal @"" should not raise an error
 | 
						|
- (void)testEmptyStringLiteralHasLocalizedState {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
  NSString *bar = @"";
 | 
						|
 | 
						|
  [testLabel setText:bar]; // no-warning
 | 
						|
}
 | 
						|
 | 
						|
// An empty string literal @"" inline should not raise an error
 | 
						|
- (void)testInlineEmptyStringLiteralHasLocalizedState {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
  [testLabel setText:@""]; // no-warning
 | 
						|
}
 | 
						|
 | 
						|
// An string literal @"Hello" inline should raise an error
 | 
						|
- (void)testInlineStringLiteralHasLocalizedState {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
  [testLabel setText:@"Hello"]; // expected-warning {{User-facing text should use localized string macro}}
 | 
						|
}
 | 
						|
 | 
						|
// A nil string should not raise an error
 | 
						|
- (void)testNilStringIsNotMarkedAsUnlocalized {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
  [testLabel setText:nil]; // no-warning
 | 
						|
}
 | 
						|
 | 
						|
// A method that takes in a localized string and returns a string
 | 
						|
// most likely that string is localized.
 | 
						|
- (void)testLocalizedStringArgument {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
  NSString *localizedString = NSLocalizedString(@"Hello", @"Comment");
 | 
						|
 | 
						|
  NSString *combinedString =
 | 
						|
      [NSString localizedStringWithFormat:@"%@", localizedString];
 | 
						|
 | 
						|
  [testLabel setText:combinedString]; // no-warning
 | 
						|
}
 | 
						|
 | 
						|
// A String passed in as a an parameter should not be considered
 | 
						|
// unlocalized
 | 
						|
- (void)testLocalizedStringAsArgument:(NSString *)argumentString {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
 | 
						|
  [testLabel setText:argumentString]; // no-warning
 | 
						|
}
 | 
						|
 | 
						|
// The warning is expected to be seen in localizedStringAsArgument: body
 | 
						|
- (void)testLocalizedStringAsArgumentOtherMethod:(NSString *)argumentString {
 | 
						|
  [self localizedStringAsArgument:@"UnlocalizedString"];
 | 
						|
}
 | 
						|
 | 
						|
// A String passed into another method that calls a method that
 | 
						|
// requires a localized string should give an error
 | 
						|
- (void)localizedStringAsArgument:(NSString *)argumentString {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
 | 
						|
  [testLabel setText:argumentString]; // expected-warning {{User-facing text should use localized string macro}}
 | 
						|
}
 | 
						|
 | 
						|
// [LocalizationTestSuite unLocalizedStringMethod] returns an unlocalized string
 | 
						|
// so we expect an error. Unfrtunately, it probably doesn't make a difference
 | 
						|
// what [LocalizationTestSuite unLocalizedStringMethod] returns since all
 | 
						|
// string values returned are marked as Unlocalized in aggressive reporting.
 | 
						|
- (void)testUnLocalizedStringMethod {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
  NSString *bar = NSLocalizedString(@"Hello", @"Comment");
 | 
						|
 | 
						|
  [testLabel setText:[LocalizationTestSuite unLocalizedStringMethod]]; // expected-warning {{User-facing text should use localized string macro}}
 | 
						|
}
 | 
						|
 | 
						|
// This is the reverse situation: accessibilitySetIdentification: doesn't care
 | 
						|
// about localization so we don't expect a warning
 | 
						|
- (void)testMethodNotInRequiresLocalizedStringMethods {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
 | 
						|
  [testLabel accessibilitySetIdentification:@"UnlocalizedString"]; // no-warning
 | 
						|
}
 | 
						|
 | 
						|
// An NSView subclass should raise a warning for methods in NSView that 
 | 
						|
// require localized strings 
 | 
						|
- (void)testRequiresLocalizationMethodFromSuperclass {
 | 
						|
  NSViewSubclass *s = [[NSViewSubclass alloc] init];
 | 
						|
  NSString *bar = @"UnlocalizedString";
 | 
						|
 | 
						|
  [s setToolTip:bar]; // expected-warning {{User-facing text should use localized string macro}}
 | 
						|
}
 | 
						|
 | 
						|
- (void)testRequiresLocalizationMethodFromProtocol {
 | 
						|
  UILabel *testLabel = [[UILabel alloc] init];
 | 
						|
 | 
						|
  [testLabel setAccessibilityLabel:@"UnlocalizedString"]; // expected-warning {{User-facing text should use localized string macro}}
 | 
						|
}
 | 
						|
 | 
						|
// EmptyLocalizationContextChecker tests
 | 
						|
#define HOM(s) YOLOC(s)
 | 
						|
#define YOLOC(x) NSLocalizedString(x, nil)
 | 
						|
 | 
						|
- (void)testNilLocalizationContext {
 | 
						|
  NSString *string = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
  NSString *string2 = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
  NSString *string3 = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
}
 | 
						|
 | 
						|
- (void)testEmptyLocalizationContext {
 | 
						|
  NSString *string = NSLocalizedString(@"LocalizedString", @""); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
  NSString *string2 = NSLocalizedString(@"LocalizedString", @" "); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
  NSString *string3 = NSLocalizedString(@"LocalizedString", @"	 "); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
}
 | 
						|
 | 
						|
- (void)testNSLocalizedStringVariants {
 | 
						|
  NSString *string = NSLocalizedStringFromTable(@"LocalizedString", nil, @""); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
  NSString *string2 = NSLocalizedStringFromTableInBundle(@"LocalizedString", nil, [[NSBundle alloc] init],@""); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
  NSString *string3 = NSLocalizedStringWithDefaultValue(@"LocalizedString", nil, [[NSBundle alloc] init], nil,@""); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
}
 | 
						|
 | 
						|
- (void)testMacroExpansionNilString {
 | 
						|
  NSString *string = YOLOC(@"Hello"); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
  NSString *string2 = HOM(@"Hello");  // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
  NSString *string3 = NSLocalizedString((0 ? @"Critical" : @"Current"),nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
}
 | 
						|
 | 
						|
- (void)testMacroExpansionDefinedInPCH {
 | 
						|
  NSString *string = MyLocalizedStringInPCH(@"Hello"); // expected-warning {{Localized string macro should include a non-empty comment for translators}}
 | 
						|
}
 | 
						|
 | 
						|
#define KCLocalizedString(x,comment) NSLocalizedString(x, comment)
 | 
						|
#define POSSIBLE_FALSE_POSITIVE(s,other) KCLocalizedString(s,@"Comment")
 | 
						|
 | 
						|
- (void)testNoWarningForNilCommentPassedIntoOtherMacro {
 | 
						|
  NSString *string = KCLocalizedString(@"Hello",@""); // no-warning
 | 
						|
  NSString *string2 = KCLocalizedString(@"Hello",nil); // no-warning
 | 
						|
  NSString *string3 = KCLocalizedString(@"Hello",@"Comment"); // no-warning
 | 
						|
}
 | 
						|
 | 
						|
- (void)testPossibleFalsePositiveSituationAbove {
 | 
						|
  NSString *string = POSSIBLE_FALSE_POSITIVE(@"Hello", nil); // no-warning
 | 
						|
  NSString *string2 = POSSIBLE_FALSE_POSITIVE(@"Hello", @"Hello"); // no-warning
 | 
						|
}
 | 
						|
 | 
						|
- (void)testTakesLocalizedString {
 | 
						|
  NSString *localized = NSLocalizedString(@"Hello", @"World");
 | 
						|
  NSString *alsoLocalized = [LocalizationTestSuite takesLocalizedString:localized]; // no-warning
 | 
						|
  NSString *stillLocalized = [LocalizationTestSuite takesLocalizedString:alsoLocalized]; // no-warning
 | 
						|
  takesLocalizedString(stillLocalized); // no-warning
 | 
						|
 | 
						|
  [LocalizationTestSuite takesLocalizedString:@"not localized"]; // expected-warning {{User-facing text should use localized string macro}}
 | 
						|
  takesLocalizedString(@"not localized"); // expected-warning {{User-facing text should use localized string macro}}
 | 
						|
}
 | 
						|
@end
 | 
						|
 | 
						|
@interface SynthesizedAccessors : NSObject
 | 
						|
@property (assign) NSObject *obj;
 | 
						|
@end
 | 
						|
 | 
						|
@implementation SynthesizedAccessors
 | 
						|
// no-crash
 | 
						|
@end
 |