r308012 - [analyzer] Add annotation for functions taking user-facing strings
Erik Verbruggen via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 14 03:24:36 PDT 2017
Author: erikjv
Date: Fri Jul 14 03:24:36 2017
New Revision: 308012
URL: http://llvm.org/viewvc/llvm-project?rev=308012&view=rev
Log:
[analyzer] Add annotation for functions taking user-facing strings
There was already a returns_localized_nsstring annotation to indicate
that the return value could be passed to UIKit methods that would
display them. However, those UIKit methods were hard-coded, and it was
not possible to indicate that other classes/methods in a code-base would
do the same.
The takes_localized_nsstring annotation can be put on function
parameters and selector parameters to indicate that those will also show
the string to the user.
Differential Revision: https://reviews.llvm.org/D35186
Modified:
cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
cfe/trunk/test/Analysis/localization-aggressive.m
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp?rev=308012&r1=308011&r2=308012&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp Fri Jul 14 03:24:36 2017
@@ -57,7 +57,7 @@ public:
};
class NonLocalizedStringChecker
- : public Checker<check::PostCall, check::PreObjCMessage,
+ : public Checker<check::PreCall, check::PostCall, check::PreObjCMessage,
check::PostObjCMessage,
check::PostStmt<ObjCStringLiteral>> {
@@ -79,9 +79,10 @@ class NonLocalizedStringChecker
void setNonLocalizedState(SVal S, CheckerContext &C) const;
void setLocalizedState(SVal S, CheckerContext &C) const;
- bool isAnnotatedAsLocalized(const Decl *D) const;
- void reportLocalizationError(SVal S, const ObjCMethodCall &M,
- CheckerContext &C, int argumentNumber = 0) const;
+ bool isAnnotatedAsReturningLocalized(const Decl *D) const;
+ bool isAnnotatedAsTakingLocalized(const Decl *D) const;
+ void reportLocalizationError(SVal S, const CallEvent &M, CheckerContext &C,
+ int argumentNumber = 0) const;
int getLocalizedArgumentForSelector(const IdentifierInfo *Receiver,
Selector S) const;
@@ -97,6 +98,7 @@ public:
void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
void checkPostStmt(const ObjCStringLiteral *SL, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
};
@@ -644,7 +646,8 @@ void NonLocalizedStringChecker::initLocS
/// Checks to see if the method / function declaration includes
/// __attribute__((annotate("returns_localized_nsstring")))
-bool NonLocalizedStringChecker::isAnnotatedAsLocalized(const Decl *D) const {
+bool NonLocalizedStringChecker::isAnnotatedAsReturningLocalized(
+ const Decl *D) const {
if (!D)
return false;
return std::any_of(
@@ -654,6 +657,19 @@ bool NonLocalizedStringChecker::isAnnota
});
}
+/// Checks to see if the method / function declaration includes
+/// __attribute__((annotate("takes_localized_nsstring")))
+bool NonLocalizedStringChecker::isAnnotatedAsTakingLocalized(
+ const Decl *D) const {
+ if (!D)
+ return false;
+ return std::any_of(
+ D->specific_attr_begin<AnnotateAttr>(),
+ D->specific_attr_end<AnnotateAttr>(), [](const AnnotateAttr *Ann) {
+ return Ann->getAnnotation() == "takes_localized_nsstring";
+ });
+}
+
/// Returns true if the given SVal is marked as Localized in the program state
bool NonLocalizedStringChecker::hasLocalizedState(SVal S,
CheckerContext &C) const {
@@ -733,8 +749,7 @@ static bool isDebuggingContext(CheckerCo
/// Reports a localization error for the passed in method call and SVal
void NonLocalizedStringChecker::reportLocalizationError(
- SVal S, const ObjCMethodCall &M, CheckerContext &C,
- int argumentNumber) const {
+ SVal S, const CallEvent &M, CheckerContext &C, int argumentNumber) const {
// Don't warn about localization errors in classes and methods that
// may be debug code.
@@ -832,7 +847,21 @@ void NonLocalizedStringChecker::checkPre
}
}
- if (argumentNumber < 0) // There was no match in UIMethods
+ if (argumentNumber < 0) { // There was no match in UIMethods
+ if (const Decl *D = msg.getDecl()) {
+ if (const ObjCMethodDecl *OMD = dyn_cast_or_null<ObjCMethodDecl>(D)) {
+ auto formals = OMD->parameters();
+ for (unsigned i = 0, ei = formals.size(); i != ei; ++i) {
+ if (isAnnotatedAsTakingLocalized(formals[i])) {
+ argumentNumber = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (argumentNumber < 0) // Still no match
return;
SVal svTitle = msg.getArgSVal(argumentNumber);
@@ -855,6 +884,25 @@ void NonLocalizedStringChecker::checkPre
}
}
+void NonLocalizedStringChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const Decl *D = Call.getDecl();
+ if (D && isa<FunctionDecl>(D)) {
+ const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
+ auto formals = FD->parameters();
+ for (unsigned i = 0,
+ ei = std::min(unsigned(formals.size()), Call.getNumArgs());
+ i != ei; ++i) {
+ if (isAnnotatedAsTakingLocalized(formals[i])) {
+ auto actual = Call.getArgSVal(i);
+ if (hasNonLocalizedState(actual, C)) {
+ reportLocalizationError(actual, Call, C, i + 1);
+ }
+ }
+ }
+ }
+}
+
static inline bool isNSStringType(QualType T, ASTContext &Ctx) {
const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
@@ -906,7 +954,7 @@ void NonLocalizedStringChecker::checkPos
const IdentifierInfo *Identifier = Call.getCalleeIdentifier();
SVal sv = Call.getReturnValue();
- if (isAnnotatedAsLocalized(D) || LSF.count(Identifier) != 0) {
+ if (isAnnotatedAsReturningLocalized(D) || LSF.count(Identifier) != 0) {
setLocalizedState(sv, C);
} else if (isNSStringType(RT, C.getASTContext()) &&
!hasLocalizedState(sv, C)) {
@@ -940,7 +988,8 @@ void NonLocalizedStringChecker::checkPos
std::pair<const IdentifierInfo *, Selector> MethodDescription = {odInfo, S};
- if (LSM.count(MethodDescription) || isAnnotatedAsLocalized(msg.getDecl())) {
+ if (LSM.count(MethodDescription) ||
+ isAnnotatedAsReturningLocalized(msg.getDecl())) {
SVal sv = msg.getReturnValue();
setLocalizedState(sv, C);
}
Modified: cfe/trunk/test/Analysis/localization-aggressive.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/localization-aggressive.m?rev=308012&r1=308011&r2=308012&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/localization-aggressive.m (original)
+++ cfe/trunk/test/Analysis/localization-aggressive.m Fri Jul 14 03:24:36 2017
@@ -61,8 +61,16 @@ int random();
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
@@ -75,6 +83,8 @@ NSString *ForceLocalized(NSString *str)
return str;
}
++ (NSString *) takesLocalizedString:(NSString *)str { return str; }
+
// An ObjC method that returns a localized string
+ (NSString *)unLocalizedStringMethod {
return @"UnlocalizedString";
@@ -269,4 +279,13 @@ NSString *ForceLocalized(NSString *str)
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
More information about the cfe-commits
mailing list