<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Sorry about the breakage! I’ve reverted the commit in <span style="font-family: Menlo; font-size: 11px;" class="">r244394.</span><div class=""><font face="Menlo" class=""><span style="font-size: 11px;" class=""><br class=""></span></font></div><div class=""><font face="Menlo" class=""><span style="font-size: 11px;" class="">Anna.</span></font></div><div class=""><font face="Menlo" class=""><span style="font-size: 11px;" class=""><br class=""></span></font><div><blockquote type="cite" class=""><div class="">On Aug 7, 2015, at 9:08 PM, Chandler Carruth <<a href="mailto:chandlerc@google.com" class="">chandlerc@google.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class="">Note that this is still failing on bots even after the windows fix:<div class=""><br class=""></div><div class=""><a href="http://lab.llvm.org:8011/builders/clang-x86_64-ubuntu-gdb-75/builds/24169" class="">http://lab.llvm.org:8011/builders/clang-x86_64-ubuntu-gdb-75/builds/24169</a><br class=""></div></div><br class=""><div class="gmail_quote"><div dir="ltr" class="">On Fri, Aug 7, 2015 at 6:50 PM Anna Zaks via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" class="">cfe-commits@lists.llvm.org</a>> wrote:<br class=""></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: zaks<br class="">
Date: Fri Aug  7 20:49:26 2015<br class="">
New Revision: 244389<br class="">
<br class="">
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=244389&view=rev" rel="noreferrer" target="_blank" class="">http://llvm.org/viewvc/llvm-project?rev=244389&view=rev</a><br class="">
Log:<br class="">
[analyzer] Add checkers for OS X / iOS localizability issues<br class="">
<br class="">
Add checkers that detect code-level localizability issues for OS X / iOS:<br class="">
 - A path sensitive checker that warns about uses of non-localized<br class="">
 NSStrings passed to UI methods expecting localized strings.<br class="">
 - A syntax checker that warns against not including a comment in<br class="">
 NSLocalizedString macros.<br class="">
<br class="">
A patch by Kulpreet Chilana!<br class="">
<br class="">
Added:<br class="">
    cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp<br class="">
    cfe/trunk/test/Analysis/localization-aggressive.m<br class="">
    cfe/trunk/test/Analysis/localization.m<br class="">
Modified:<br class="">
    cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt<br class="">
    cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td<br class="">
<br class="">
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt<br class="">
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt?rev=244389&r1=244388&r2=244389&view=diff" rel="noreferrer" target="_blank" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt?rev=244389&r1=244388&r2=244389&view=diff</a><br class="">
==============================================================================<br class="">
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt (original)<br class="">
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt Fri Aug  7 20:49:26 2015<br class="">
@@ -39,6 +39,7 @@ add_clang_library(clangStaticAnalyzerChe<br class="">
   IdenticalExprChecker.cpp<br class="">
   IvarInvalidationChecker.cpp<br class="">
   LLVMConventionsChecker.cpp<br class="">
+  LocalizationChecker.cpp<br class="">
   MacOSKeychainAPIChecker.cpp<br class="">
   MacOSXAPIChecker.cpp<br class="">
   MallocChecker.cpp<br class="">
<br class="">
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td<br class="">
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td?rev=244389&r1=244388&r2=244389&view=diff" rel="noreferrer" target="_blank" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td?rev=244389&r1=244388&r2=244389&view=diff</a><br class="">
==============================================================================<br class="">
--- cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td (original)<br class="">
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td Fri Aug  7 20:49:26 2015<br class="">
@@ -452,6 +452,14 @@ def DirectIvarAssignmentForAnnotatedFunc<br class="">
   HelpText<"Check for direct assignments to instance variables in the methods annotated with objc_no_direct_instance_variable_assignment">,<br class="">
   DescFile<"DirectIvarAssignment.cpp">;<br class="">
<br class="">
+def NonLocalizedStringChecker : Checker<"NonLocalizedStringChecker">,<br class="">
+  HelpText<"Warns about uses of non-localized NSStrings passed to UI methods expecting localized NSStrings">,<br class="">
+  DescFile<"LocalizationChecker.cpp">;<br class="">
+<br class="">
+def EmptyLocalizationContextChecker : Checker<"EmptyLocalizationContextChecker">,<br class="">
+  HelpText<"Check that NSLocalizedString macros include a comment for context">,<br class="">
+  DescFile<"LocalizationChecker.cpp">;<br class="">
+<br class="">
 } // end "alpha.osx.cocoa"<br class="">
<br class="">
 let ParentPackage = CoreFoundation in {<br class="">
<br class="">
Added: cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp<br class="">
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp?rev=244389&view=auto" rel="noreferrer" target="_blank" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp?rev=244389&view=auto</a><br class="">
==============================================================================<br class="">
--- cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp (added)<br class="">
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp Fri Aug  7 20:49:26 2015<br class="">
@@ -0,0 +1,559 @@<br class="">
+//=- LocalizationChecker.cpp -------------------------------------*- C++ -*-==//<br class="">
+//<br class="">
+//                     The LLVM Compiler Infrastructure<br class="">
+//<br class="">
+// This file is distributed under the University of Illinois Open Source<br class="">
+// License. See LICENSE.TXT for details.<br class="">
+//<br class="">
+//===----------------------------------------------------------------------===//<br class="">
+//<br class="">
+//  This file defines a set of checks for localizability including:<br class="">
+//  1) A checker that warns about uses of non-localized NSStrings passed to<br class="">
+//     UI methods expecting localized strings<br class="">
+//  2) A syntactic checker that warns against the bad practice of<br class="">
+//     not including a comment in NSLocalizedString macros.<br class="">
+//<br class="">
+//===----------------------------------------------------------------------===//<br class="">
+<br class="">
+#include "ClangSACheckers.h"<br class="">
+#include "SelectorExtras.h"<br class="">
+#include "clang/AST/Attr.h"<br class="">
+#include "clang/AST/Decl.h"<br class="">
+#include "clang/AST/DeclObjC.h"<br class="">
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"<br class="">
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"<br class="">
+#include "clang/StaticAnalyzer/Core/Checker.h"<br class="">
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"<br class="">
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"<br class="">
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"<br class="">
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"<br class="">
+#include "clang/Lex/Lexer.h"<br class="">
+#include "clang/AST/RecursiveASTVisitor.h"<br class="">
+#include "clang/AST/StmtVisitor.h"<br class="">
+#include "llvm/Support/Unicode.h"<br class="">
+<br class="">
+using namespace clang;<br class="">
+using namespace ento;<br class="">
+<br class="">
+namespace {<br class="">
+struct LocalizedState {<br class="">
+private:<br class="">
+  enum Kind { NonLocalized, Localized } K;<br class="">
+  LocalizedState(Kind InK) : K(InK) {}<br class="">
+<br class="">
+public:<br class="">
+  bool isLocalized() const { return K == Localized; }<br class="">
+  bool isNonLocalized() const { return K == NonLocalized; }<br class="">
+<br class="">
+  static LocalizedState getLocalized() { return LocalizedState(Localized); }<br class="">
+  static LocalizedState getNonLocalized() {<br class="">
+    return LocalizedState(NonLocalized);<br class="">
+  }<br class="">
+<br class="">
+  // Overload the == operator<br class="">
+  bool operator==(const LocalizedState &X) const { return K == X.K; }<br class="">
+<br class="">
+  // LLVMs equivalent of a hash function<br class="">
+  void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }<br class="">
+};<br class="">
+<br class="">
+class NonLocalizedStringChecker<br class="">
+    : public Checker<check::PostCall, check::PreObjCMessage,<br class="">
+                     check::PostObjCMessage,<br class="">
+                     check::PostStmt<ObjCStringLiteral>> {<br class="">
+<br class="">
+  mutable std::unique_ptr<BugType> BT;<br class="">
+<br class="">
+  // Methods that require a localized string<br class="">
+  mutable std::map<StringRef, std::map<StringRef, uint8_t>> UIMethods;<br class="">
+  // Methods that return a localized string<br class="">
+  mutable llvm::SmallSet<std::pair<StringRef, StringRef>, 10> LSM;<br class="">
+  // C Functions that return a localized string<br class="">
+  mutable llvm::StringMap<char> LSF;<br class="">
+<br class="">
+  void initUIMethods(ASTContext &Ctx) const;<br class="">
+  void initLocStringsMethods(ASTContext &Ctx) const;<br class="">
+<br class="">
+  bool hasNonLocalizedState(SVal S, CheckerContext &C) const;<br class="">
+  bool hasLocalizedState(SVal S, CheckerContext &C) const;<br class="">
+  void setNonLocalizedState(SVal S, CheckerContext &C) const;<br class="">
+  void setLocalizedState(SVal S, CheckerContext &C) const;<br class="">
+<br class="">
+  bool isAnnotatedAsLocalized(const Decl *D) const;<br class="">
+  void reportLocalizationError(SVal S, const ObjCMethodCall &M,<br class="">
+                               CheckerContext &C, int argumentNumber = 0) const;<br class="">
+<br class="">
+public:<br class="">
+  NonLocalizedStringChecker();<br class="">
+<br class="">
+  // When this parameter is set to true, the checker assumes all<br class="">
+  // methods that return NSStrings are unlocalized. Thus, more false<br class="">
+  // positives will be reported.<br class="">
+  DefaultBool IsAggressive;<br class="">
+<br class="">
+  void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;<br class="">
+  void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;<br class="">
+  void checkPostStmt(const ObjCStringLiteral *SL, CheckerContext &C) const;<br class="">
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;<br class="">
+};<br class="">
+<br class="">
+} // end anonymous namespace<br class="">
+<br class="">
+REGISTER_MAP_WITH_PROGRAMSTATE(LocalizedMemMap, const MemRegion *,<br class="">
+                               LocalizedState)<br class="">
+<br class="">
+NonLocalizedStringChecker::NonLocalizedStringChecker() {<br class="">
+  BT.reset(new BugType(this, "Unlocalized string", "Localizability Error"));<br class="">
+}<br class="">
+<br class="">
+/// Initializes a list of methods that require a localized string<br class="">
+/// Format: {"ClassName", {{"selectorName:", LocStringArg#}, ...}, ...}<br class="">
+void NonLocalizedStringChecker::initUIMethods(ASTContext &Ctx) const {<br class="">
+  if (!UIMethods.empty())<br class="">
+    return;<br class="">
+<br class="">
+  // TODO: This should eventually be a comprehensive list of UIKit methods<br class="">
+<br class="">
+  UIMethods = {{"UILabel", {{"setText:", 0}}},<br class="">
+               {"UIButton", {{"setText:", 0}}},<br class="">
+               {"NSButton", {{"setTitle:", 0}}},<br class="">
+               {"NSButtonCell", {{"setTitle:", 0}}},<br class="">
+               {"NSMenuItem", {{"setTitle:", 0}}},<br class="">
+               {"UIAlertAction", {{"actionWithTitle:style:handler:", 0}}},<br class="">
+               {"UIAlertController",<br class="">
+                {{"alertControllerWithTitle:message:preferredStyle:", 1}}},<br class="">
+               {"NSAttributedString",<br class="">
+                {{"initWithString:", 0}, {"initWithString:attributes:", 0}}}};<br class="">
+}<br class="">
+<br class="">
+/// Initializes a list of methods and C functions that return a localized string<br class="">
+void NonLocalizedStringChecker::initLocStringsMethods(ASTContext &Ctx) const {<br class="">
+  if (!LSM.empty())<br class="">
+    return;<br class="">
+<br class="">
+  LSM.insert({"NSBundle", "localizedStringForKey:value:table:"});<br class="">
+  LSM.insert({"NSDateFormatter", "stringFromDate:"});<br class="">
+  LSM.insert(<br class="">
+      {"NSDateFormatter", "localizedStringFromDate:dateStyle:timeStyle:"});<br class="">
+  LSM.insert({"NSNumberFormatter", "stringFromNumber:"});<br class="">
+  LSM.insert({"UITextField", "text"});<br class="">
+  LSM.insert({"UITextView", "text"});<br class="">
+  LSM.insert({"UILabel", "text"});<br class="">
+<br class="">
+  LSF.insert({"CFDateFormatterCreateStringWithDate", '\0'});<br class="">
+  LSF.insert({"CFDateFormatterCreateStringWithAbsoluteTime", '\0'});<br class="">
+  LSF.insert({"CFNumberFormatterCreateStringWithNumber", '\0'});<br class="">
+}<br class="">
+<br class="">
+/// Checks to see if the method / function declaration includes<br class="">
+/// __attribute__((annotate("returns_localized_nsstring")))<br class="">
+bool NonLocalizedStringChecker::isAnnotatedAsLocalized(const Decl *D) const {<br class="">
+  return std::any_of(<br class="">
+      D->specific_attr_begin<AnnotateAttr>(),<br class="">
+      D->specific_attr_end<AnnotateAttr>(), [](const AnnotateAttr *Ann) {<br class="">
+        return Ann->getAnnotation() == "returns_localized_nsstring";<br class="">
+      });<br class="">
+}<br class="">
+<br class="">
+/// Returns true if the given SVal is marked as Localized in the program state<br class="">
+bool NonLocalizedStringChecker::hasLocalizedState(SVal S,<br class="">
+                                                  CheckerContext &C) const {<br class="">
+  const MemRegion *mt = S.getAsRegion();<br class="">
+  if (mt) {<br class="">
+    const LocalizedState *LS = C.getState()->get<LocalizedMemMap>(mt);<br class="">
+    if (LS && LS->isLocalized())<br class="">
+      return true;<br class="">
+  }<br class="">
+  return false;<br class="">
+}<br class="">
+<br class="">
+/// Returns true if the given SVal is marked as NonLocalized in the program<br class="">
+/// state<br class="">
+bool NonLocalizedStringChecker::hasNonLocalizedState(SVal S,<br class="">
+                                                     CheckerContext &C) const {<br class="">
+  const MemRegion *mt = S.getAsRegion();<br class="">
+  if (mt) {<br class="">
+    const LocalizedState *LS = C.getState()->get<LocalizedMemMap>(mt);<br class="">
+    if (LS && LS->isNonLocalized())<br class="">
+      return true;<br class="">
+  }<br class="">
+  return false;<br class="">
+}<br class="">
+<br class="">
+/// Marks the given SVal as Localized in the program state<br class="">
+void NonLocalizedStringChecker::setLocalizedState(const SVal S,<br class="">
+                                                  CheckerContext &C) const {<br class="">
+  const MemRegion *mt = S.getAsRegion();<br class="">
+  if (mt) {<br class="">
+    ProgramStateRef State =<br class="">
+        C.getState()->set<LocalizedMemMap>(mt, LocalizedState::getLocalized());<br class="">
+    C.addTransition(State);<br class="">
+  }<br class="">
+}<br class="">
+<br class="">
+/// Marks the given SVal as NonLocalized in the program state<br class="">
+void NonLocalizedStringChecker::setNonLocalizedState(const SVal S,<br class="">
+                                                     CheckerContext &C) const {<br class="">
+  const MemRegion *mt = S.getAsRegion();<br class="">
+  if (mt) {<br class="">
+    ProgramStateRef State = C.getState()->set<LocalizedMemMap>(<br class="">
+        mt, LocalizedState::getNonLocalized());<br class="">
+    C.addTransition(State);<br class="">
+  }<br class="">
+}<br class="">
+<br class="">
+/// Reports a localization error for the passed in method call and SVal<br class="">
+void NonLocalizedStringChecker::reportLocalizationError(<br class="">
+    SVal S, const ObjCMethodCall &M, CheckerContext &C,<br class="">
+    int argumentNumber) const {<br class="">
+<br class="">
+  ExplodedNode *ErrNode = C.getPredecessor();<br class="">
+  static CheckerProgramPointTag Tag("NonLocalizedStringChecker",<br class="">
+                                    "UnlocalizedString");<br class="">
+  ErrNode = C.addTransition(C.getState(), C.getPredecessor(), &Tag);<br class="">
+<br class="">
+  if (!ErrNode)<br class="">
+    return;<br class="">
+<br class="">
+  // Generate the bug report.<br class="">
+  std::unique_ptr<BugReport> R(<br class="">
+      new BugReport(*BT, "String should be localized", ErrNode));<br class="">
+  if (argumentNumber) {<br class="">
+    R->addRange(M.getArgExpr(argumentNumber - 1)->getSourceRange());<br class="">
+  } else {<br class="">
+    R->addRange(M.getSourceRange());<br class="">
+  }<br class="">
+  R->markInteresting(S);<br class="">
+  C.emitReport(std::move(R));<br class="">
+}<br class="">
+<br class="">
+/// Check if the string being passed in has NonLocalized state<br class="">
+void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg,<br class="">
+                                                    CheckerContext &C) const {<br class="">
+  initUIMethods(C.getASTContext());<br class="">
+<br class="">
+  const ObjCInterfaceDecl *OD = msg.getReceiverInterface();<br class="">
+  if (!OD)<br class="">
+    return;<br class="">
+  const IdentifierInfo *odInfo = OD->getIdentifier();<br class="">
+<br class="">
+  Selector S = msg.getSelector();<br class="">
+<br class="">
+  StringRef Name(S.getAsString());<br class="">
+  assert(!Name.empty());<br class="">
+<br class="">
+  auto method = UIMethods.find(odInfo->getName());<br class="">
+  if (odInfo->isStr("NSString")) {<br class="">
+    // Handle the case where the receiver is an NSString<br class="">
+    // These special NSString methods draw to the screen<br class="">
+<br class="">
+    if (!(Name.startswith("drawAtPoint") || Name.startswith("drawInRect") ||<br class="">
+          Name.startswith("drawWithRect")))<br class="">
+      return;<br class="">
+<br class="">
+    SVal svTitle = msg.getReceiverSVal();<br class="">
+<br class="">
+    bool isNonLocalized = hasNonLocalizedState(svTitle, C);<br class="">
+<br class="">
+    if (isNonLocalized) {<br class="">
+      reportLocalizationError(svTitle, msg, C);<br class="">
+    }<br class="">
+  } else if (method != UIMethods.end()) {<br class="">
+<br class="">
+    std::map<StringRef, uint8_t> m = method->second;<br class="">
+<br class="">
+    auto argumentIterator = m.find(Name);<br class="">
+<br class="">
+    if (argumentIterator == m.end())<br class="">
+      return;<br class="">
+<br class="">
+    int argumentNumber = argumentIterator->second;<br class="">
+<br class="">
+    SVal svTitle = msg.getArgSVal(argumentNumber);<br class="">
+<br class="">
+    if (const ObjCStringRegion *SR =<br class="">
+            dyn_cast_or_null<ObjCStringRegion>(svTitle.getAsRegion())) {<br class="">
+      StringRef stringValue =<br class="">
+          SR->getObjCStringLiteral()->getString()->getString();<br class="">
+      if ((stringValue.trim().size() == 0 && stringValue.size() > 0) ||<br class="">
+          stringValue.empty())<br class="">
+        return;<br class="">
+      if (!IsAggressive && llvm::sys::unicode::columnWidthUTF8(stringValue) < 2)<br class="">
+        return;<br class="">
+    }<br class="">
+<br class="">
+    bool isNonLocalized = hasNonLocalizedState(svTitle, C);<br class="">
+<br class="">
+    if (isNonLocalized) {<br class="">
+      reportLocalizationError(svTitle, msg, C, argumentNumber + 1);<br class="">
+    }<br class="">
+  }<br class="">
+}<br class="">
+<br class="">
+static inline bool isNSStringType(QualType T, ASTContext &Ctx) {<br class="">
+<br class="">
+  const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();<br class="">
+  if (!PT)<br class="">
+    return false;<br class="">
+<br class="">
+  ObjCInterfaceDecl *Cls = PT->getObjectType()->getInterface();<br class="">
+  if (!Cls)<br class="">
+    return false;<br class="">
+<br class="">
+  IdentifierInfo *ClsName = Cls->getIdentifier();<br class="">
+<br class="">
+  // FIXME: Should we walk the chain of classes?<br class="">
+  return ClsName == &Ctx.Idents.get("NSString") ||<br class="">
+         ClsName == &Ctx.Idents.get("NSMutableString");<br class="">
+}<br class="">
+<br class="">
+/// Marks a string being returned by any call as localized<br class="">
+/// if it is in LocStringFunctions (LSF) or the function is annotated.<br class="">
+/// Otherwise, we mark it as NonLocalized (Aggressive) or<br class="">
+/// NonLocalized only if it is not backed by a SymRegion (Non-Aggressive),<br class="">
+/// basically leaving only string literals as NonLocalized.<br class="">
+void NonLocalizedStringChecker::checkPostCall(const CallEvent &Call,<br class="">
+                                              CheckerContext &C) const {<br class="">
+  initLocStringsMethods(C.getASTContext());<br class="">
+<br class="">
+  if (!Call.getOriginExpr())<br class="">
+    return;<br class="">
+<br class="">
+  // Anything that takes in a localized NSString as an argument<br class="">
+  // and returns an NSString will be assumed to be returning a<br class="">
+  // localized NSString. (Counter: Incorrectly combining two LocalizedStrings)<br class="">
+  const QualType RT = Call.getResultType();<br class="">
+  if (isNSStringType(RT, C.getASTContext())) {<br class="">
+    for (unsigned i = 0; i < Call.getNumArgs(); ++i) {<br class="">
+      SVal argValue = Call.getArgSVal(i);<br class="">
+      if (hasLocalizedState(argValue, C)) {<br class="">
+        SVal sv = Call.getReturnValue();<br class="">
+        setLocalizedState(sv, C);<br class="">
+        return;<br class="">
+      }<br class="">
+    }<br class="">
+  }<br class="">
+<br class="">
+  const Decl *D = Call.getDecl();<br class="">
+  if (!D)<br class="">
+    return;<br class="">
+<br class="">
+  StringRef IdentifierName = C.getCalleeName(D->getAsFunction());<br class="">
+<br class="">
+  SVal sv = Call.getReturnValue();<br class="">
+  if (isAnnotatedAsLocalized(D) || LSF.find(IdentifierName) != LSF.end()) {<br class="">
+    setLocalizedState(sv, C);<br class="">
+  } else if (isNSStringType(RT, C.getASTContext()) &&<br class="">
+             !hasLocalizedState(sv, C)) {<br class="">
+    if (IsAggressive) {<br class="">
+      setNonLocalizedState(sv, C);<br class="">
+    } else {<br class="">
+      const SymbolicRegion *SymReg =<br class="">
+          dyn_cast_or_null<SymbolicRegion>(sv.getAsRegion());<br class="">
+      if (!SymReg)<br class="">
+        setNonLocalizedState(sv, C);<br class="">
+    }<br class="">
+  }<br class="">
+}<br class="">
+<br class="">
+/// Marks a string being returned by an ObjC method as localized<br class="">
+/// if it is in LocStringMethods or the method is annotated<br class="">
+void NonLocalizedStringChecker::checkPostObjCMessage(const ObjCMethodCall &msg,<br class="">
+                                                     CheckerContext &C) const {<br class="">
+  initLocStringsMethods(C.getASTContext());<br class="">
+<br class="">
+  if (!msg.isInstanceMessage())<br class="">
+    return;<br class="">
+<br class="">
+  const ObjCInterfaceDecl *OD = msg.getReceiverInterface();<br class="">
+  if (!OD)<br class="">
+    return;<br class="">
+  const IdentifierInfo *odInfo = OD->getIdentifier();<br class="">
+<br class="">
+  StringRef IdentifierName = odInfo->getName();<br class="">
+<br class="">
+  Selector S = msg.getSelector();<br class="">
+  StringRef SelectorName = S.getAsString();<br class="">
+  assert(!SelectorName.empty());<br class="">
+<br class="">
+  std::pair<StringRef, StringRef> MethodDescription = {IdentifierName,<br class="">
+                                                       SelectorName};<br class="">
+<br class="">
+  if (LSM.count(MethodDescription) || isAnnotatedAsLocalized(msg.getDecl())) {<br class="">
+    SVal sv = msg.getReturnValue();<br class="">
+    setLocalizedState(sv, C);<br class="">
+  }<br class="">
+}<br class="">
+<br class="">
+/// Marks all empty string literals as localized<br class="">
+void NonLocalizedStringChecker::checkPostStmt(const ObjCStringLiteral *SL,<br class="">
+                                              CheckerContext &C) const {<br class="">
+  SVal sv = C.getSVal(SL);<br class="">
+  setNonLocalizedState(sv, C);<br class="">
+}<br class="">
+<br class="">
+namespace {<br class="">
+class EmptyLocalizationContextChecker<br class="">
+    : public Checker<check::ASTDecl<ObjCImplementationDecl>> {<br class="">
+<br class="">
+  // A helper class, which walks the AST<br class="">
+  class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {<br class="">
+    const ObjCMethodDecl *MD;<br class="">
+    BugReporter &BR;<br class="">
+    AnalysisManager &Mgr;<br class="">
+    const CheckerBase *Checker;<br class="">
+    LocationOrAnalysisDeclContext DCtx;<br class="">
+<br class="">
+  public:<br class="">
+    MethodCrawler(const ObjCMethodDecl *InMD, BugReporter &InBR,<br class="">
+                  const CheckerBase *Checker, AnalysisManager &InMgr,<br class="">
+                  AnalysisDeclContext *InDCtx)<br class="">
+        : MD(InMD), BR(InBR), Mgr(InMgr), Checker(Checker), DCtx(InDCtx) {}<br class="">
+<br class="">
+    void VisitStmt(const Stmt *S) { VisitChildren(S); }<br class="">
+<br class="">
+    void VisitObjCMessageExpr(const ObjCMessageExpr *ME);<br class="">
+<br class="">
+    void reportEmptyContextError(const ObjCMessageExpr *M) const;<br class="">
+<br class="">
+    void VisitChildren(const Stmt *S) {<br class="">
+      for (const Stmt *Child : S->children()) {<br class="">
+        if (Child)<br class="">
+          this->Visit(Child);<br class="">
+      }<br class="">
+    }<br class="">
+  };<br class="">
+<br class="">
+public:<br class="">
+  void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,<br class="">
+                    BugReporter &BR) const;<br class="">
+};<br class="">
+} // end anonymous namespace<br class="">
+<br class="">
+void EmptyLocalizationContextChecker::checkASTDecl(<br class="">
+    const ObjCImplementationDecl *D, AnalysisManager &Mgr,<br class="">
+    BugReporter &BR) const {<br class="">
+<br class="">
+  for (const ObjCMethodDecl *M : D->methods()) {<br class="">
+    AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);<br class="">
+<br class="">
+    const Stmt *Body = M->getBody();<br class="">
+    assert(Body);<br class="">
+<br class="">
+    MethodCrawler MC(M->getCanonicalDecl(), BR, this, Mgr, DCtx);<br class="">
+    MC.VisitStmt(Body);<br class="">
+  }<br class="">
+}<br class="">
+<br class="">
+/// This check attempts to match these macros, assuming they are defined as<br class="">
+/// follows:<br class="">
+///<br class="">
+/// #define NSLocalizedString(key, comment) \<br class="">
+/// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]<br class="">
+/// #define NSLocalizedStringFromTable(key, tbl, comment) \<br class="">
+/// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)]<br class="">
+/// #define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \<br class="">
+/// [bundle localizedStringForKey:(key) value:@"" table:(tbl)]<br class="">
+/// #define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment)<br class="">
+///<br class="">
+/// We cannot use the path sensitive check because the macro argument we are<br class="">
+/// checking for (comment) is not used and thus not present in the AST,<br class="">
+/// so we use Lexer on the original macro call and retrieve the value of<br class="">
+/// the comment. If it's empty or nil, we raise a warning.<br class="">
+void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr(<br class="">
+    const ObjCMessageExpr *ME) {<br class="">
+<br class="">
+  const ObjCInterfaceDecl *OD = ME->getReceiverInterface();<br class="">
+  if (!OD)<br class="">
+    return;<br class="">
+<br class="">
+  const IdentifierInfo *odInfo = OD->getIdentifier();<br class="">
+<br class="">
+  if (!(odInfo->isStr("NSBundle") ||<br class="">
+        StringRef(ME->getSelector().getAsString())<br class="">
+            .equals("localizedStringForKey:value:table:"))) {<br class="">
+    return;<br class="">
+  }<br class="">
+<br class="">
+  SourceRange R = ME->getSourceRange();<br class="">
+  if (!R.getBegin().isMacroID())<br class="">
+    return;<br class="">
+<br class="">
+  // getImmediateMacroCallerLoc gets the location of the immediate macro<br class="">
+  // caller, one level up the stack toward the initial macro typed into the<br class="">
+  // source, so SL should point to the NSLocalizedString macro.<br class="">
+  SourceLocation SL =<br class="">
+      Mgr.getSourceManager().getImmediateMacroCallerLoc(R.getBegin());<br class="">
+  std::pair<FileID, unsigned> SLInfo =<br class="">
+      Mgr.getSourceManager().getDecomposedLoc(SL);<br class="">
+<br class="">
+  SrcMgr::SLocEntry SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first);<br class="">
+<br class="">
+  // If NSLocalizedString macro is wrapped in another macro, we need to<br class="">
+  // unwrap the expansion until we get to the NSLocalizedStringMacro.<br class="">
+  while (SE.isExpansion()) {<br class="">
+    SL = SE.getExpansion().getSpellingLoc();<br class="">
+    SLInfo = Mgr.getSourceManager().getDecomposedLoc(SL);<br class="">
+    SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first);<br class="">
+  }<br class="">
+<br class="">
+  llvm::MemoryBuffer *BF = SE.getFile().getContentCache()->getRawBuffer();<br class="">
+  Lexer TheLexer(SL, LangOptions(), BF->getBufferStart(),<br class="">
+                 BF->getBufferStart() + SLInfo.second, BF->getBufferEnd());<br class="">
+<br class="">
+  Token I;<br class="">
+  Token Result;    // This will hold the token just before the last ')'<br class="">
+  int p_count = 0; // This is for parenthesis matching<br class="">
+  while (!TheLexer.LexFromRawLexer(I)) {<br class="">
+    if (I.getKind() == tok::l_paren)<br class="">
+      ++p_count;<br class="">
+    if (I.getKind() == tok::r_paren) {<br class="">
+      if (p_count == 1)<br class="">
+        break;<br class="">
+      --p_count;<br class="">
+    }<br class="">
+    Result = I;<br class="">
+  }<br class="">
+<br class="">
+  if (isAnyIdentifier(Result.getKind())) {<br class="">
+    if (Result.getRawIdentifier().equals("nil")) {<br class="">
+      reportEmptyContextError(ME);<br class="">
+      return;<br class="">
+    }<br class="">
+  }<br class="">
+<br class="">
+  if (!isStringLiteral(Result.getKind()))<br class="">
+    return;<br class="">
+<br class="">
+  StringRef Comment =<br class="">
+      StringRef(Result.getLiteralData(), Result.getLength()).trim("\"");<br class="">
+<br class="">
+  if ((Comment.trim().size() == 0 && Comment.size() > 0) || // Is Whitespace<br class="">
+      Comment.empty()) {<br class="">
+    reportEmptyContextError(ME);<br class="">
+  }<br class="">
+}<br class="">
+<br class="">
+void EmptyLocalizationContextChecker::MethodCrawler::reportEmptyContextError(<br class="">
+    const ObjCMessageExpr *ME) const {<br class="">
+  // Generate the bug report.<br class="">
+  BR.EmitBasicReport(MD, Checker, "Context Missing", "Localizability Error",<br class="">
+                     "Localized string macro should include a non-empty "<br class="">
+                     "comment for translators",<br class="">
+                     PathDiagnosticLocation(ME, BR.getSourceManager(), DCtx));<br class="">
+}<br class="">
+<br class="">
+//===----------------------------------------------------------------------===//<br class="">
+// Checker registration.<br class="">
+//===----------------------------------------------------------------------===//<br class="">
+<br class="">
+void ento::registerNonLocalizedStringChecker(CheckerManager &mgr) {<br class="">
+  NonLocalizedStringChecker *checker =<br class="">
+      mgr.registerChecker<NonLocalizedStringChecker>();<br class="">
+  checker->IsAggressive =<br class="">
+      mgr.getAnalyzerOptions().getBooleanOption("AggressiveReport", false);<br class="">
+}<br class="">
+<br class="">
+void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) {<br class="">
+  mgr.registerChecker<EmptyLocalizationContextChecker>();<br class="">
+}<br class="">
\ No newline at end of file<br class="">
<br class="">
Added: cfe/trunk/test/Analysis/localization-aggressive.m<br class="">
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/localization-aggressive.m?rev=244389&view=auto" rel="noreferrer" target="_blank" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/localization-aggressive.m?rev=244389&view=auto</a><br class="">
==============================================================================<br class="">
--- cfe/trunk/test/Analysis/localization-aggressive.m (added)<br class="">
+++ cfe/trunk/test/Analysis/localization-aggressive.m Fri Aug  7 20:49:26 2015<br class="">
@@ -0,0 +1,243 @@<br class="">
+// RUN: %clang_cc1 -analyze -fblocks -analyzer-store=region  -analyzer-checker=alpha.osx.cocoa.NonLocalizedStringChecker -analyzer-checker=alpha.osx.cocoa.EmptyLocalizationContextChecker -verify  -analyzer-config AggressiveReport=true %s<br class="">
+<br class="">
+// These declarations were reduced using Delta-Debugging from Foundation.h<br class="">
+// on Mac OS X.<br class="">
+<br class="">
+#define nil ((id)0)<br class="">
+#define NSLocalizedString(key, comment)                                        \<br class="">
+  [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]<br class="">
+#define NSLocalizedStringFromTable(key, tbl, comment)                          \<br class="">
+  [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)]<br class="">
+#define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment)          \<br class="">
+  [bundle localizedStringForKey:(key) value:@"" table:(tbl)]<br class="">
+#define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment)      \<br class="">
+  [bundle localizedStringForKey:(key) value:(val) table:(tbl)]<br class="">
+#define CGFLOAT_TYPE double<br class="">
+typedef CGFLOAT_TYPE CGFloat;<br class="">
+struct CGPoint {<br class="">
+  CGFloat x;<br class="">
+  CGFloat y;<br class="">
+};<br class="">
+typedef struct CGPoint CGPoint;<br class="">
+@interface NSObject<br class="">
++ (id)alloc;<br class="">
+- (id)init;<br class="">
+@end<br class="">
+@class NSDictionary;<br class="">
+@interface NSString : NSObject<br class="">
+- (void)drawAtPoint:(CGPoint)point withAttributes:(NSDictionary *)attrs;<br class="">
++ (instancetype)localizedStringWithFormat:(NSString *)format, ...;<br class="">
+@end<br class="">
+@interface NSBundle : NSObject<br class="">
++ (NSBundle *)mainBundle;<br class="">
+- (NSString *)localizedStringForKey:(NSString *)key<br class="">
+                              value:(NSString *)value<br class="">
+                              table:(NSString *)tableName;<br class="">
+@end<br class="">
+@interface UILabel : NSObject<br class="">
+@property(nullable, nonatomic, copy) NSString *text;<br class="">
+- (void)accessibilitySetIdentification:(NSString *)ident;<br class="">
+@end<br class="">
+@interface TestObject : NSObject<br class="">
+@property(strong) NSString *text;<br class="">
+@end<br class="">
+<br class="">
+@interface LocalizationTestSuite : NSObject<br class="">
+NSString *ForceLocalized(NSString *str)<br class="">
+    __attribute__((annotate("returns_localized_nsstring")));<br class="">
+CGPoint CGPointMake(CGFloat x, CGFloat y);<br class="">
+int random();<br class="">
+// This next one is a made up API<br class="">
+NSString *CFNumberFormatterCreateStringWithNumber(float x);<br class="">
++ (NSString *)forceLocalized:(NSString *)str<br class="">
+    __attribute__((annotate("returns_localized_nsstring")));<br class="">
+@end<br class="">
+<br class="">
+// Test cases begin here<br class="">
+@implementation LocalizationTestSuite<br class="">
+<br class="">
+// A C-Funtion that returns a localized string because it has the<br class="">
+// "returns_localized_nsstring" annotation<br class="">
+NSString *ForceLocalized(NSString *str) { return str; }<br class="">
+// An ObjC method that returns a localized string because it has the<br class="">
+// "returns_localized_nsstring" annotation<br class="">
++ (NSString *)forceLocalized:(NSString *)str {<br class="">
+  return str;<br class="">
+}<br class="">
+<br class="">
+// An ObjC method that returns a localized string<br class="">
++ (NSString *)unLocalizedStringMethod {<br class="">
+  return @"UnlocalizedString";<br class="">
+}<br class="">
+<br class="">
+- (void)testLocalizationErrorDetectedOnPathway {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  NSString *bar = NSLocalizedString(@"Hello", @"Comment");<br class="">
+<br class="">
+  if (random()) {<br class="">
+    bar = @"Unlocalized string";<br class="">
+  }<br class="">
+<br class="">
+  [testLabel setText:bar]; // expected-warning {{String should be localized}}<br class="">
+}<br class="">
+<br class="">
+- (void)testLocalizationErrorDetectedOnNSString {<br class="">
+  NSString *bar = NSLocalizedString(@"Hello", @"Comment");<br class="">
+<br class="">
+  if (random()) {<br class="">
+    bar = @"Unlocalized string";<br class="">
+  }<br class="">
+<br class="">
+  [bar drawAtPoint:CGPointMake(0, 0) withAttributes:nil]; // expected-warning {{String should be localized}}<br class="">
+}<br class="">
+<br class="">
+- (void)testNoLocalizationErrorDetectedFromCFunction {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  NSString *bar = CFNumberFormatterCreateStringWithNumber(1);<br class="">
+<br class="">
+  [testLabel setText:bar]; // no-warning<br class="">
+}<br class="">
+<br class="">
+- (void)testAnnotationAddsLocalizedStateForCFunction {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  NSString *bar = NSLocalizedString(@"Hello", @"Comment");<br class="">
+<br class="">
+  if (random()) {<br class="">
+    bar = @"Unlocalized string";<br class="">
+  }<br class="">
+<br class="">
+  [testLabel setText:ForceLocalized(bar)]; // no-warning<br class="">
+}<br class="">
+<br class="">
+- (void)testAnnotationAddsLocalizedStateForObjCMethod {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  NSString *bar = NSLocalizedString(@"Hello", @"Comment");<br class="">
+<br class="">
+  if (random()) {<br class="">
+    bar = @"Unlocalized string";<br class="">
+  }<br class="">
+<br class="">
+  [testLabel setText:[LocalizationTestSuite forceLocalized:bar]]; // no-warning<br class="">
+}<br class="">
+<br class="">
+// An empty string literal @"" should not raise an error<br class="">
+- (void)testEmptyStringLiteralHasLocalizedState {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  NSString *bar = @"";<br class="">
+<br class="">
+  [testLabel setText:bar]; // no-warning<br class="">
+}<br class="">
+<br class="">
+// An empty string literal @"" inline should not raise an error<br class="">
+- (void)testInlineEmptyStringLiteralHasLocalizedState {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  [testLabel setText:@""]; // no-warning<br class="">
+}<br class="">
+<br class="">
+// An string literal @"Hello" inline should raise an error<br class="">
+- (void)testInlineStringLiteralHasLocalizedState {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  [testLabel setText:@"Hello"]; // expected-warning {{String should be localized}}<br class="">
+}<br class="">
+<br class="">
+// A nil string should not raise an error<br class="">
+- (void)testNilStringIsNotMarkedAsUnlocalized {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  [testLabel setText:nil]; // no-warning<br class="">
+}<br class="">
+<br class="">
+// A method that takes in a localized string and returns a string<br class="">
+// most likely that string is localized.<br class="">
+- (void)testLocalizedStringArgument {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  NSString *localizedString = NSLocalizedString(@"Hello", @"Comment");<br class="">
+<br class="">
+  NSString *combinedString =<br class="">
+      [NSString localizedStringWithFormat:@"%@", localizedString];<br class="">
+<br class="">
+  [testLabel setText:combinedString]; // no-warning<br class="">
+}<br class="">
+<br class="">
+// A String passed in as a an parameter should not be considered<br class="">
+// unlocalized<br class="">
+- (void)testLocalizedStringAsArgument:(NSString *)argumentString {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+<br class="">
+  [testLabel setText:argumentString]; // no-warning<br class="">
+}<br class="">
+<br class="">
+// A String passed into another method that calls a method that<br class="">
+// requires a localized string should give an error<br class="">
+- (void)localizedStringAsArgument:(NSString *)argumentString {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+<br class="">
+  [testLabel setText:argumentString]; // expected-warning {{String should be localized}}<br class="">
+}<br class="">
+<br class="">
+// The warning is expected to be seen in localizedStringAsArgument: body<br class="">
+- (void)testLocalizedStringAsArgumentOtherMethod:(NSString *)argumentString {<br class="">
+  [self localizedStringAsArgument:@"UnlocalizedString"];<br class="">
+}<br class="">
+<br class="">
+// [LocalizationTestSuite unLocalizedStringMethod] returns an unlocalized string<br class="">
+// so we expect an error. Unfrtunately, it probably doesn't make a difference<br class="">
+// what [LocalizationTestSuite unLocalizedStringMethod] returns since all<br class="">
+// string values returned are marked as Unlocalized in aggressive reporting.<br class="">
+- (void)testUnLocalizedStringMethod {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  NSString *bar = NSLocalizedString(@"Hello", @"Comment");<br class="">
+<br class="">
+  [testLabel setText:[LocalizationTestSuite unLocalizedStringMethod]]; // expected-warning {{String should be localized}}<br class="">
+}<br class="">
+<br class="">
+// This is the reverse situation: accessibilitySetIdentification: doesn't care<br class="">
+// about localization so we don't expect a warning<br class="">
+- (void)testMethodNotInRequiresLocalizedStringMethods {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+<br class="">
+  [testLabel accessibilitySetIdentification:@"UnlocalizedString"]; // no-warning<br class="">
+}<br class="">
+<br class="">
+// EmptyLocalizationContextChecker tests<br class="">
+#define HOM(s) YOLOC(s)<br class="">
+#define YOLOC(x) NSLocalizedString(x, nil)<br class="">
+<br class="">
+- (void)testNilLocalizationContext {<br class="">
+  NSString *string = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}}<br class="">
+  NSString *string2 = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}}<br class="">
+  NSString *string3 = NSLocalizedString(@"LocalizedString", nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}}<br class="">
+}<br class="">
+<br class="">
+- (void)testEmptyLocalizationContext {<br class="">
+  NSString *string = NSLocalizedString(@"LocalizedString", @""); // expected-warning {{Localized string macro should include a non-empty comment for translators}}<br class="">
+  NSString *string2 = NSLocalizedString(@"LocalizedString", @" "); // expected-warning {{Localized string macro should include a non-empty comment for translators}}<br class="">
+  NSString *string3 = NSLocalizedString(@"LocalizedString", @"  "); // expected-warning {{Localized string macro should include a non-empty comment for translators}}<br class="">
+}<br class="">
+<br class="">
+- (void)testNSLocalizedStringVariants {<br class="">
+  NSString *string = NSLocalizedStringFromTable(@"LocalizedString", nil, @""); // expected-warning {{Localized string macro should include a non-empty comment for translators}}<br class="">
+  NSString *string2 = NSLocalizedStringFromTableInBundle(@"LocalizedString", nil, [[NSBundle alloc] init],@""); // expected-warning {{Localized string macro should include a non-empty comment for translators}}<br class="">
+  NSString *string3 = NSLocalizedStringWithDefaultValue(@"LocalizedString", nil, [[NSBundle alloc] init], nil,@""); // expected-warning {{Localized string macro should include a non-empty comment for translators}}<br class="">
+}<br class="">
+<br class="">
+- (void)testMacroExpansionNilString {<br class="">
+  NSString *string = YOLOC(@"Hello"); // expected-warning {{Localized string macro should include a non-empty comment for translators}}<br class="">
+  NSString *string2 = HOM(@"Hello");  // expected-warning {{Localized string macro should include a non-empty comment for translators}}<br class="">
+  NSString *string3 = NSLocalizedString((0 ? @"Critical" : @"Current"),nil); // expected-warning {{Localized string macro should include a non-empty comment for translators}}<br class="">
+}<br class="">
+<br class="">
+#define KCLocalizedString(x,comment) NSLocalizedString(x, comment)<br class="">
+#define POSSIBLE_FALSE_POSITIVE(s,other) KCLocalizedString(s,@"Comment")<br class="">
+<br class="">
+- (void)testNoWarningForNilCommentPassedIntoOtherMacro {<br class="">
+  NSString *string = KCLocalizedString(@"Hello",@""); // no-warning<br class="">
+  NSString *string2 = KCLocalizedString(@"Hello",nil); // no-warning<br class="">
+  NSString *string3 = KCLocalizedString(@"Hello",@"Comment"); // no-warning<br class="">
+}<br class="">
+<br class="">
+- (void)testPossibleFalsePositiveSituationAbove {<br class="">
+  NSString *string = POSSIBLE_FALSE_POSITIVE(@"Hello", nil); // no-warning<br class="">
+  NSString *string2 = POSSIBLE_FALSE_POSITIVE(@"Hello", @"Hello"); // no-warning<br class="">
+}<br class="">
+<br class="">
+@end<br class="">
<br class="">
Added: cfe/trunk/test/Analysis/localization.m<br class="">
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/localization.m?rev=244389&view=auto" rel="noreferrer" target="_blank" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/localization.m?rev=244389&view=auto</a><br class="">
==============================================================================<br class="">
--- cfe/trunk/test/Analysis/localization.m (added)<br class="">
+++ cfe/trunk/test/Analysis/localization.m Fri Aug  7 20:49:26 2015<br class="">
@@ -0,0 +1,86 @@<br class="">
+// RUN: %clang_cc1 -analyze -fblocks -analyzer-store=region -analyzer-checker=alpha.osx.cocoa.NonLocalizedStringChecker -analyzer-checker=alpha.osx.cocoa.EmptyLocalizationContextChecker -verify  %s<br class="">
+<br class="">
+// The larger set of tests in located in localization.m. These are tests<br class="">
+// specific for non-aggressive reporting.<br class="">
+<br class="">
+// These declarations were reduced using Delta-Debugging from Foundation.h<br class="">
+// on Mac OS X.<br class="">
+<br class="">
+#define nil ((id)0)<br class="">
+#define NSLocalizedString(key, comment)                                        \<br class="">
+  [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]<br class="">
+#define NSLocalizedStringFromTable(key, tbl, comment)                          \<br class="">
+  [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)]<br class="">
+#define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment)          \<br class="">
+  [bundle localizedStringForKey:(key) value:@"" table:(tbl)]<br class="">
+#define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment)      \<br class="">
+  [bundle localizedStringForKey:(key) value:(val) table:(tbl)]<br class="">
+@interface NSObject<br class="">
++ (id)alloc;<br class="">
+- (id)init;<br class="">
+@end<br class="">
+@interface NSString : NSObject<br class="">
+@end<br class="">
+@interface NSBundle : NSObject<br class="">
++ (NSBundle *)mainBundle;<br class="">
+- (NSString *)localizedStringForKey:(NSString *)key<br class="">
+                              value:(NSString *)value<br class="">
+                              table:(NSString *)tableName;<br class="">
+@end<br class="">
+@interface UILabel : NSObject<br class="">
+@property(nullable, nonatomic, copy) NSString *text;<br class="">
+@end<br class="">
+@interface TestObject : NSObject<br class="">
+@property(strong) NSString *text;<br class="">
+@end<br class="">
+<br class="">
+@interface LocalizationTestSuite : NSObject<br class="">
+int random();<br class="">
+@end<br class="">
+<br class="">
+// Test cases begin here<br class="">
+@implementation LocalizationTestSuite<br class="">
+<br class="">
+// An object passed in as an parameter's string member<br class="">
+// should not be considered unlocalized<br class="">
+- (void)testObjectAsArgument:(TestObject *)argumentObject {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+<br class="">
+  [testLabel setText:[argumentObject text]]; // no-warning<br class="">
+  [testLabel setText:argumentObject.text];   // no-warning<br class="">
+}<br class="">
+<br class="">
+- (void)testLocalizationErrorDetectedOnPathway {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  NSString *bar = NSLocalizedString(@"Hello", @"Comment");<br class="">
+<br class="">
+  if (random()) {<br class="">
+    bar = @"Unlocalized string";<br class="">
+  }<br class="">
+<br class="">
+  [testLabel setText:bar]; // expected-warning {{String should be localized}}<br class="">
+}<br class="">
+<br class="">
+- (void)testOneCharacterStringsDoNotGiveAWarning {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  NSString *bar = NSLocalizedString(@"Hello", @"Comment");<br class="">
+<br class="">
+  if (random()) {<br class="">
+    bar = @"-";<br class="">
+  }<br class="">
+<br class="">
+  [testLabel setText:bar]; // no-warning<br class="">
+}<br class="">
+<br class="">
+- (void)testOneCharacterUTFStringsDoNotGiveAWarning {<br class="">
+  UILabel *testLabel = [[UILabel alloc] init];<br class="">
+  NSString *bar = NSLocalizedString(@"Hello", @"Comment");<br class="">
+<br class="">
+  if (random()) {<br class="">
+    bar = @"—";<br class="">
+  }<br class="">
+<br class="">
+  [testLabel setText:bar]; // no-warning<br class="">
+}<br class="">
+<br class="">
+@end<br class="">
<br class="">
<br class="">
_______________________________________________<br class="">
cfe-commits mailing list<br class="">
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank" class="">cfe-commits@lists.llvm.org</a><br class="">
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank" class="">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br class="">
</blockquote></div>
</div></blockquote></div><br class=""></div></body></html>