Author: dcoughlin Date: Thu Apr 28 14:44:40 2016 New Revision: 267924 URL: http://llvm.org/viewvc/llvm-project?rev=267924&view=rev Log: [analyzer] Add path note for localizability checker.
Add a path note indicating the location of the non-localized string literal in NonLocalizedStringChecker. rdar://problem/25981525 Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp cfe/trunk/test/Analysis/localization.m Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp?rev=267924&r1=267923&r2=267924&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp (original) +++ cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp Thu Apr 28 14:44:40 2016 @@ -111,6 +111,30 @@ NonLocalizedStringChecker::NonLocalizedS "Localizability Issue (Apple)")); } +namespace { +class NonLocalizedStringBRVisitor final + : public BugReporterVisitorImpl<NonLocalizedStringBRVisitor> { + + const MemRegion *NonLocalizedString; + bool Satisfied; + +public: + NonLocalizedStringBRVisitor(const MemRegion *NonLocalizedString) + : NonLocalizedString(NonLocalizedString), Satisfied(false) { + assert(NonLocalizedString); + } + + PathDiagnosticPiece *VisitNode(const ExplodedNode *Succ, + const ExplodedNode *Pred, + BugReporterContext &BRC, + BugReport &BR) override; + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.Add(NonLocalizedString); + } +}; +} // End anonymous namespace. + #define NEW_RECEIVER(receiver) \ llvm::DenseMap<Selector, uint8_t> &receiver##M = \ UIMethods.insert({&Ctx.Idents.get(#receiver), \ @@ -676,6 +700,11 @@ void NonLocalizedStringChecker::reportLo R->addRange(M.getSourceRange()); } R->markInteresting(S); + + const MemRegion *StringRegion = S.getAsRegion(); + if (StringRegion) + R->addVisitor(llvm::make_unique<NonLocalizedStringBRVisitor>(StringRegion)); + C.emitReport(std::move(R)); } @@ -866,6 +895,41 @@ void NonLocalizedStringChecker::checkPos setNonLocalizedState(sv, C); } +PathDiagnosticPiece * +NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ, + const ExplodedNode *Pred, + BugReporterContext &BRC, BugReport &BR) { + if (Satisfied) + return nullptr; + + Optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>(); + if (!Point.hasValue()) + return nullptr; + + auto *LiteralExpr = dyn_cast<ObjCStringLiteral>(Point->getStmt()); + if (!LiteralExpr) + return nullptr; + + ProgramStateRef State = Succ->getState(); + SVal LiteralSVal = State->getSVal(LiteralExpr, Succ->getLocationContext()); + if (LiteralSVal.getAsRegion() != NonLocalizedString) + return nullptr; + + Satisfied = true; + + PathDiagnosticLocation L = + PathDiagnosticLocation::create(*Point, BRC.getSourceManager()); + + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + + auto *Piece = new PathDiagnosticEventPiece(L, + "Non-localized string literal here"); + Piece->addRange(LiteralExpr->getSourceRange()); + + return Piece; +} + namespace { class EmptyLocalizationContextChecker : public Checker<check::ASTDecl<ObjCImplementationDecl>> { Modified: cfe/trunk/test/Analysis/localization.m URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/localization.m?rev=267924&r1=267923&r2=267924&view=diff ============================================================================== --- cfe/trunk/test/Analysis/localization.m (original) +++ cfe/trunk/test/Analysis/localization.m Thu Apr 28 14:44:40 2016 @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -fblocks -analyzer-store=region -analyzer-checker=optin.osx.cocoa.localizability.NonLocalizedStringChecker -analyzer-checker=alpha.osx.cocoa.localizability.PluralMisuseChecker -verify %s +// RUN: %clang_cc1 -analyze -fblocks -analyzer-store=region -analyzer-output=text -analyzer-checker=optin.osx.cocoa.localizability.NonLocalizedStringChecker -analyzer-checker=alpha.osx.cocoa.localizability.PluralMisuseChecker -verify %s // The larger set of tests in located in localization.m. These are tests // specific for non-aggressive reporting. @@ -61,11 +61,26 @@ NSString *KHLocalizedString(NSString* ke UILabel *testLabel = [[UILabel alloc] init]; NSString *bar = NSLocalizedString(@"Hello", @"Comment"); - if (random()) { - bar = @"Unlocalized string"; + if (random()) { // expected-note {{Taking true branch}} + bar = @"Unlocalized string"; // expected-note {{Non-localized string literal here}} } - [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}} + [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}} expected-note {{User-facing}} +} + +- (void)testMultipleUnlocalizedStringsInSamePath { + UILabel *testLabel = [[UILabel alloc] init]; + NSString *bar = @"Unlocalized string"; // no-note + + bar = @"Unlocalized string"; // expected-note {{Non-localized string literal here}} + + NSString *other = @"Other unlocalized string."; // no-note + (void)other; + + NSString *same = @"Unlocalized string"; // no-note + (void)same; + + [testLabel setText:bar]; // expected-warning {{User-facing text should use localized string macro}} expected-note {{User-facing}} } - (void)testOneCharacterStringsDoNotGiveAWarning { @@ -103,14 +118,14 @@ NSString *KHLocalizedString(NSString* ke - (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_PLURAL"); // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} expected-note {{Plural}} } 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 [NSString stringWithFormat:@"%@, %@", @"Test", (numOfReminders != 1) ? [NSString stringWithFormat:NSLocalizedString(@"%@ Reminders", @"Plural count of reminders"), numOfReminders] : [NSString stringWithFormat:NSLocalizedString(@"1 reminder", @"One reminder")]]; // expected-warning 2 {{Plural cases are not supported accross all languages. Use a .stringsdict file}} expected-note 2 {{Plural}} } return nil; } @@ -119,18 +134,18 @@ NSString *KHLocalizedString(NSString* ke 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}} + 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}} expected-note {{Plural}} } else { - count = [count stringByAppendingFormat:@"%@", KHLocalizedString(@"New Story", @"One new story")]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} + count = [count stringByAppendingFormat:@"%@", KHLocalizedString(@"New Story", @"One new story")]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} expected-note {{Plural}} } } - (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}} + return [NSString stringWithFormat:KHLocalizedString(@"value.singular",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} expected-note {{Plural}} } else { - return [NSString stringWithFormat:KHLocalizedString(@"value.plural",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} + return [NSString stringWithFormat:KHLocalizedString(@"value.plural",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} expected-note {{Plural}} } } @@ -138,9 +153,9 @@ NSString *KHLocalizedString(NSString* ke 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}} + return [NSString stringWithFormat:KHLocalizedString(@"value.singular",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} expected-note {{Plural}} } else { - return [NSString stringWithFormat:KHLocalizedString(@"value.plural",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} + return [NSString stringWithFormat:KHLocalizedString(@"value.plural",nil), count]; // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} expected-note {{Plural}} } } @@ -154,7 +169,7 @@ NSString *KHLocalizedString(NSString* ke 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 KHLocalizedString(@"value.plural",nil); // expected-warning {{Plural cases are not supported accross all languages. Use a .stringsdict file}} expected-note {{Plural}} } return nil; } @@ -221,6 +236,6 @@ NSString *KHLocalizedString(NSString* ke @implementation MyDebugView - (void)setupScreen:(UILabel *)label { - label.text = @"Unlocalized"; + label.text = @"Unlocalized"; // no-warning } @end _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits