diff options
Diffstat (limited to 'clang/test/Analysis')
| -rw-r--r-- | clang/test/Analysis/localization-aggressive.m | 37 | ||||
| -rw-r--r-- | clang/test/Analysis/localization.m | 127 |
2 files changed, 154 insertions, 10 deletions
diff --git a/clang/test/Analysis/localization-aggressive.m b/clang/test/Analysis/localization-aggressive.m index 389f546d3d0..5f8a92b5df8 100644 --- a/clang/test/Analysis/localization-aggressive.m +++ b/clang/test/Analysis/localization-aggressive.m @@ -34,13 +34,21 @@ typedef struct CGPoint CGPoint; value:(NSString *)value table:(NSString *)tableName; @end -@interface UILabel : NSObject -@property(nullable, nonatomic, copy) NSString *text; +@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) @@ -78,7 +86,7 @@ NSString *ForceLocalized(NSString *str) { return str; } bar = @"Unlocalized string"; } - [testLabel setText:bar]; // expected-warning {{String should be localized}} + [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}} } - (void)testLocalizationErrorDetectedOnNSString { @@ -88,7 +96,7 @@ NSString *ForceLocalized(NSString *str) { return str; } bar = @"Unlocalized string"; } - [bar drawAtPoint:CGPointMake(0, 0) withAttributes:nil]; // expected-warning {{String should be localized}} + [bar drawAtPoint:CGPointMake(0, 0) withAttributes:nil]; // expected-warning {{User-facing text should use localized string macro}} } - (void)testNoLocalizationErrorDetectedFromCFunction { @@ -137,7 +145,7 @@ NSString *ForceLocalized(NSString *str) { return str; } // An string literal @"Hello" inline should raise an error - (void)testInlineStringLiteralHasLocalizedState { UILabel *testLabel = [[UILabel alloc] init]; - [testLabel setText:@"Hello"]; // expected-warning {{String should be localized}} + [testLabel setText:@"Hello"]; // expected-warning {{User-facing text should use localized string macro}} } // A nil string should not raise an error @@ -176,7 +184,7 @@ NSString *ForceLocalized(NSString *str) { return str; } - (void)localizedStringAsArgument:(NSString *)argumentString { UILabel *testLabel = [[UILabel alloc] init]; - [testLabel setText:argumentString]; // expected-warning {{String should be localized}} + [testLabel setText:argumentString]; // expected-warning {{User-facing text should use localized string macro}} } // [LocalizationTestSuite unLocalizedStringMethod] returns an unlocalized string @@ -187,7 +195,7 @@ NSString *ForceLocalized(NSString *str) { return str; } UILabel *testLabel = [[UILabel alloc] init]; NSString *bar = NSLocalizedString(@"Hello", @"Comment"); - [testLabel setText:[LocalizationTestSuite unLocalizedStringMethod]]; // expected-warning {{String should be localized}} + [testLabel setText:[LocalizationTestSuite unLocalizedStringMethod]]; // expected-warning {{User-facing text should use localized string macro}} } // This is the reverse situation: accessibilitySetIdentification: doesn't care @@ -198,6 +206,21 @@ NSString *ForceLocalized(NSString *str) { return str; } [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) diff --git a/clang/test/Analysis/localization.m b/clang/test/Analysis/localization.m index 429b0d79747..9270a092c1f 100644 --- a/clang/test/Analysis/localization.m +++ b/clang/test/Analysis/localization.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -fblocks -analyzer-store=region -analyzer-checker=alpha.osx.cocoa.NonLocalizedStringChecker -analyzer-checker=alpha.osx.cocoa.EmptyLocalizationContextChecker -verify %s +// RUN: %clang_cc1 -analyze -fblocks -analyzer-store=region -analyzer-checker=alpha.osx.cocoa.NonLocalizedStringChecker -analyzer-checker=alpha.osx.cocoa.PluralMisuseChecker -verify %s // The larger set of tests in located in localization.m. These are tests // specific for non-aggressive reporting. @@ -20,6 +20,8 @@ - (id)init; @end @interface NSString : NSObject +- (NSString *)stringByAppendingFormat:(NSString *)format, ...; ++ (instancetype)stringWithFormat:(NSString *)format, ...; @end @interface NSBundle : NSObject + (NSBundle *)mainBundle; @@ -36,11 +38,16 @@ @interface LocalizationTestSuite : NSObject int random(); +@property (assign) int unreadArticlesCount; @end - +#define MCLocalizedString(s) NSLocalizedString(s,nil); // Test cases begin here @implementation LocalizationTestSuite +NSString *KHLocalizedString(NSString* key, NSString* comment) { + return NSLocalizedString(key, comment); +} + // An object passed in as an parameter's string member // should not be considered unlocalized - (void)testObjectAsArgument:(TestObject *)argumentObject { @@ -58,7 +65,7 @@ int random(); bar = @"Unlocalized string"; } - [testLabel setText:bar]; // expected-warning {{String should be localized}} + [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}} } - (void)testOneCharacterStringsDoNotGiveAWarning { @@ -83,4 +90,118 @@ int random(); [testLabel setText:bar]; // no-warning } +// Plural Misuse Checker Tests +// These tests are modeled off incorrect uses of the many-one pattern +// from real projects. + +- (NSString *)test1:(int)plural { + if (plural) { + return MCLocalizedString(@"TYPE_PLURAL"); // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} + } + return MCLocalizedString(@"TYPE"); +} + +- (NSString *)test2:(int)numOfReminders { + if (numOfReminders > 0) { + return [NSString stringWithFormat:@"%@, %@", @"Test", (numOfReminders != 1) ? [NSString stringWithFormat:NSLocalizedString(@"%@ Reminders", @"Plural count of reminders"), numOfReminders] : [NSString stringWithFormat:NSLocalizedString(@"1 reminder", @"One reminder")]]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} + } + return nil; +} + +- (void)test3 { + NSString *count; + if (self.unreadArticlesCount > 1) + { + count = [count stringByAppendingFormat:@"%@", KHLocalizedString(@"New Stories", @"Plural count for new stories")]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} + } else { + count = [count stringByAppendingFormat:@"%@", KHLocalizedString(@"New Story", @"One new story")]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} + } +} + +- (NSString *)test4:(int)count { + if ( count == 1 ) + { + return [NSString stringWithFormat:KHLocalizedString(@"value.singular",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} + } else { + return [NSString stringWithFormat:KHLocalizedString(@"value.plural",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} + } +} + +- (NSString *)test5:(int)count { + int test = count == 1; + if (test) + { + return [NSString stringWithFormat:KHLocalizedString(@"value.singular",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} + } else { + return [NSString stringWithFormat:KHLocalizedString(@"value.plural",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} + } +} + +// This tests the heuristic that the direct parent IfStmt must match the isCheckingPlurality confition to avoid false positives generated from complex code (generally the pattern we're looking for is simple If-Else) + +- (NSString *)test6:(int)sectionIndex { + int someOtherVariable = 0; + if (sectionIndex == 1) + { + // Do some other crazy stuff + if (someOtherVariable) + return KHLocalizedString(@"OK",nil); // no-warning + } else { + return KHLocalizedString(@"value.plural",nil); // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} + } + return nil; +} + +// False positives that we are not accounting for involve matching the heuristic +// of having 1 or 2 in the RHS of a BinaryOperator and having a localized string +// in the body of the IfStmt. This is seen a lot when checking for the section +// indexpath of something like a UITableView + +// - (NSString *)testNotAccountedFor:(int)sectionIndex { +// if (sectionIndex == 1) +// { +// return KHLocalizedString(@"1",nil); // false-positive +// } else if (sectionIndex == 2) { +// return KHLocalizedString(@"2",nil); // false-positive +// } else if (sectionIndex == 3) { +// return KHLocalizedString(@"3",nil); // no-false-positive +// } +// } + +// Potential test-cases to support in the future + +// - (NSString *)test7:(int)count { +// BOOL plural = count != 1; +// return KHLocalizedString(plural ? @"PluralString" : @"SingularString", @""); +// } +// +// - (NSString *)test8:(BOOL)plural { +// return KHLocalizedString(([NSString stringWithFormat:@"RELATIVE_DATE_%@_%@", ((1 == 1) ? @"FUTURE" : @"PAST"), plural ? @"PLURAL" : @"SINGULAR"])); +// } +// +// +// +// - (void)test9:(int)numberOfTimesEarned { +// NSString* localizedDescriptionKey; +// if (numberOfTimesEarned == 1) { +// localizedDescriptionKey = @"SINGULAR_%@"; +// } else { +// localizedDescriptionKey = @"PLURAL_%@_%@"; +// } +// NSLocalizedString(localizedDescriptionKey, nil); +// } +// +// - (NSString *)test10 { +// NSInteger count = self.problems.count; +// NSString *title = [NSString stringWithFormat:@"%ld Problems", (long) count]; +// if (count < 2) { +// if (count == 0) { +// title = [NSString stringWithFormat:@"No Problems Found"]; +// } else { +// title = [NSString stringWithFormat:@"%ld Problem", (long) count]; +// } +// } +// return title; +// } + @end |

