summaryrefslogtreecommitdiffstats
path: root/clang/test
diff options
context:
space:
mode:
authorDevin Coughlin <dcoughlin@apple.com>2015-09-23 21:43:21 +0000
committerDevin Coughlin <dcoughlin@apple.com>2015-09-23 21:43:21 +0000
commit9f21f68bfeedd25af8be00722b47e9d5dca9de8a (patch)
treed8b328ccc5a7820bfcfb7f3db03795f5cea592a5 /clang/test
parentd784e6893cfbdd24b4f701e486adbab9906d4b27 (diff)
downloadbcm5719-llvm-9f21f68bfeedd25af8be00722b47e9d5dca9de8a.tar.gz
bcm5719-llvm-9f21f68bfeedd25af8be00722b47e9d5dca9de8a.zip
[analyzer] Improve localizability checks for iOS / OS X.
Various improvements to the localization checker: * Adjusted copy to be consistent with diagnostic text in other Apple API checkers. * Added in ~150 UIKit / AppKit methods that require localized strings in UnlocalizedStringsChecker. * UnlocalizedStringChecker now checks for UI methods up the class hierarchy and UI methods that conform for a certain Objective-C protocol. * Added in alpha version of PluralMisuseChecker and some regression tests. False positives are still not ideal. (This is the second attempt, with the memory issues on Linux resolved.) A patch by Kulpreet Chilana! Differential Revision: http://reviews.llvm.org/D12417 llvm-svn: 248432
Diffstat (limited to 'clang/test')
-rw-r--r--clang/test/Analysis/localization-aggressive.m37
-rw-r--r--clang/test/Analysis/localization.m127
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
OpenPOWER on IntegriCloud