1*e5dd7070Spatrick //=- LocalizationChecker.cpp -------------------------------------*- C++ -*-==// 2*e5dd7070Spatrick // 3*e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information. 5*e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*e5dd7070Spatrick // 7*e5dd7070Spatrick //===----------------------------------------------------------------------===// 8*e5dd7070Spatrick // 9*e5dd7070Spatrick // This file defines a set of checks for localizability including: 10*e5dd7070Spatrick // 1) A checker that warns about uses of non-localized NSStrings passed to 11*e5dd7070Spatrick // UI methods expecting localized strings 12*e5dd7070Spatrick // 2) A syntactic checker that warns against the bad practice of 13*e5dd7070Spatrick // not including a comment in NSLocalizedString macros. 14*e5dd7070Spatrick // 15*e5dd7070Spatrick //===----------------------------------------------------------------------===// 16*e5dd7070Spatrick 17*e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 18*e5dd7070Spatrick #include "clang/AST/Attr.h" 19*e5dd7070Spatrick #include "clang/AST/Decl.h" 20*e5dd7070Spatrick #include "clang/AST/DeclObjC.h" 21*e5dd7070Spatrick #include "clang/AST/RecursiveASTVisitor.h" 22*e5dd7070Spatrick #include "clang/AST/StmtVisitor.h" 23*e5dd7070Spatrick #include "clang/Lex/Lexer.h" 24*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 25*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 26*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h" 27*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h" 28*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 29*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 30*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 31*e5dd7070Spatrick #include "llvm/Support/Unicode.h" 32*e5dd7070Spatrick 33*e5dd7070Spatrick using namespace clang; 34*e5dd7070Spatrick using namespace ento; 35*e5dd7070Spatrick 36*e5dd7070Spatrick namespace { 37*e5dd7070Spatrick struct LocalizedState { 38*e5dd7070Spatrick private: 39*e5dd7070Spatrick enum Kind { NonLocalized, Localized } K; 40*e5dd7070Spatrick LocalizedState(Kind InK) : K(InK) {} 41*e5dd7070Spatrick 42*e5dd7070Spatrick public: 43*e5dd7070Spatrick bool isLocalized() const { return K == Localized; } 44*e5dd7070Spatrick bool isNonLocalized() const { return K == NonLocalized; } 45*e5dd7070Spatrick 46*e5dd7070Spatrick static LocalizedState getLocalized() { return LocalizedState(Localized); } 47*e5dd7070Spatrick static LocalizedState getNonLocalized() { 48*e5dd7070Spatrick return LocalizedState(NonLocalized); 49*e5dd7070Spatrick } 50*e5dd7070Spatrick 51*e5dd7070Spatrick // Overload the == operator 52*e5dd7070Spatrick bool operator==(const LocalizedState &X) const { return K == X.K; } 53*e5dd7070Spatrick 54*e5dd7070Spatrick // LLVMs equivalent of a hash function 55*e5dd7070Spatrick void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } 56*e5dd7070Spatrick }; 57*e5dd7070Spatrick 58*e5dd7070Spatrick class NonLocalizedStringChecker 59*e5dd7070Spatrick : public Checker<check::PreCall, check::PostCall, check::PreObjCMessage, 60*e5dd7070Spatrick check::PostObjCMessage, 61*e5dd7070Spatrick check::PostStmt<ObjCStringLiteral>> { 62*e5dd7070Spatrick 63*e5dd7070Spatrick mutable std::unique_ptr<BugType> BT; 64*e5dd7070Spatrick 65*e5dd7070Spatrick // Methods that require a localized string 66*e5dd7070Spatrick mutable llvm::DenseMap<const IdentifierInfo *, 67*e5dd7070Spatrick llvm::DenseMap<Selector, uint8_t>> UIMethods; 68*e5dd7070Spatrick // Methods that return a localized string 69*e5dd7070Spatrick mutable llvm::SmallSet<std::pair<const IdentifierInfo *, Selector>, 12> LSM; 70*e5dd7070Spatrick // C Functions that return a localized string 71*e5dd7070Spatrick mutable llvm::SmallSet<const IdentifierInfo *, 5> LSF; 72*e5dd7070Spatrick 73*e5dd7070Spatrick void initUIMethods(ASTContext &Ctx) const; 74*e5dd7070Spatrick void initLocStringsMethods(ASTContext &Ctx) const; 75*e5dd7070Spatrick 76*e5dd7070Spatrick bool hasNonLocalizedState(SVal S, CheckerContext &C) const; 77*e5dd7070Spatrick bool hasLocalizedState(SVal S, CheckerContext &C) const; 78*e5dd7070Spatrick void setNonLocalizedState(SVal S, CheckerContext &C) const; 79*e5dd7070Spatrick void setLocalizedState(SVal S, CheckerContext &C) const; 80*e5dd7070Spatrick 81*e5dd7070Spatrick bool isAnnotatedAsReturningLocalized(const Decl *D) const; 82*e5dd7070Spatrick bool isAnnotatedAsTakingLocalized(const Decl *D) const; 83*e5dd7070Spatrick void reportLocalizationError(SVal S, const CallEvent &M, CheckerContext &C, 84*e5dd7070Spatrick int argumentNumber = 0) const; 85*e5dd7070Spatrick 86*e5dd7070Spatrick int getLocalizedArgumentForSelector(const IdentifierInfo *Receiver, 87*e5dd7070Spatrick Selector S) const; 88*e5dd7070Spatrick 89*e5dd7070Spatrick public: 90*e5dd7070Spatrick NonLocalizedStringChecker(); 91*e5dd7070Spatrick 92*e5dd7070Spatrick // When this parameter is set to true, the checker assumes all 93*e5dd7070Spatrick // methods that return NSStrings are unlocalized. Thus, more false 94*e5dd7070Spatrick // positives will be reported. 95*e5dd7070Spatrick DefaultBool IsAggressive; 96*e5dd7070Spatrick 97*e5dd7070Spatrick void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 98*e5dd7070Spatrick void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 99*e5dd7070Spatrick void checkPostStmt(const ObjCStringLiteral *SL, CheckerContext &C) const; 100*e5dd7070Spatrick void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 101*e5dd7070Spatrick void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 102*e5dd7070Spatrick }; 103*e5dd7070Spatrick 104*e5dd7070Spatrick } // end anonymous namespace 105*e5dd7070Spatrick 106*e5dd7070Spatrick REGISTER_MAP_WITH_PROGRAMSTATE(LocalizedMemMap, const MemRegion *, 107*e5dd7070Spatrick LocalizedState) 108*e5dd7070Spatrick 109*e5dd7070Spatrick NonLocalizedStringChecker::NonLocalizedStringChecker() { 110*e5dd7070Spatrick BT.reset(new BugType(this, "Unlocalizable string", 111*e5dd7070Spatrick "Localizability Issue (Apple)")); 112*e5dd7070Spatrick } 113*e5dd7070Spatrick 114*e5dd7070Spatrick namespace { 115*e5dd7070Spatrick class NonLocalizedStringBRVisitor final : public BugReporterVisitor { 116*e5dd7070Spatrick 117*e5dd7070Spatrick const MemRegion *NonLocalizedString; 118*e5dd7070Spatrick bool Satisfied; 119*e5dd7070Spatrick 120*e5dd7070Spatrick public: 121*e5dd7070Spatrick NonLocalizedStringBRVisitor(const MemRegion *NonLocalizedString) 122*e5dd7070Spatrick : NonLocalizedString(NonLocalizedString), Satisfied(false) { 123*e5dd7070Spatrick assert(NonLocalizedString); 124*e5dd7070Spatrick } 125*e5dd7070Spatrick 126*e5dd7070Spatrick PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, 127*e5dd7070Spatrick BugReporterContext &BRC, 128*e5dd7070Spatrick PathSensitiveBugReport &BR) override; 129*e5dd7070Spatrick 130*e5dd7070Spatrick void Profile(llvm::FoldingSetNodeID &ID) const override { 131*e5dd7070Spatrick ID.Add(NonLocalizedString); 132*e5dd7070Spatrick } 133*e5dd7070Spatrick }; 134*e5dd7070Spatrick } // End anonymous namespace. 135*e5dd7070Spatrick 136*e5dd7070Spatrick #define NEW_RECEIVER(receiver) \ 137*e5dd7070Spatrick llvm::DenseMap<Selector, uint8_t> &receiver##M = \ 138*e5dd7070Spatrick UIMethods.insert({&Ctx.Idents.get(#receiver), \ 139*e5dd7070Spatrick llvm::DenseMap<Selector, uint8_t>()}) \ 140*e5dd7070Spatrick .first->second; 141*e5dd7070Spatrick #define ADD_NULLARY_METHOD(receiver, method, argument) \ 142*e5dd7070Spatrick receiver##M.insert( \ 143*e5dd7070Spatrick {Ctx.Selectors.getNullarySelector(&Ctx.Idents.get(#method)), argument}); 144*e5dd7070Spatrick #define ADD_UNARY_METHOD(receiver, method, argument) \ 145*e5dd7070Spatrick receiver##M.insert( \ 146*e5dd7070Spatrick {Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(#method)), argument}); 147*e5dd7070Spatrick #define ADD_METHOD(receiver, method_list, count, argument) \ 148*e5dd7070Spatrick receiver##M.insert({Ctx.Selectors.getSelector(count, method_list), argument}); 149*e5dd7070Spatrick 150*e5dd7070Spatrick /// Initializes a list of methods that require a localized string 151*e5dd7070Spatrick /// Format: {"ClassName", {{"selectorName:", LocStringArg#}, ...}, ...} 152*e5dd7070Spatrick void NonLocalizedStringChecker::initUIMethods(ASTContext &Ctx) const { 153*e5dd7070Spatrick if (!UIMethods.empty()) 154*e5dd7070Spatrick return; 155*e5dd7070Spatrick 156*e5dd7070Spatrick // UI Methods 157*e5dd7070Spatrick NEW_RECEIVER(UISearchDisplayController) 158*e5dd7070Spatrick ADD_UNARY_METHOD(UISearchDisplayController, setSearchResultsTitle, 0) 159*e5dd7070Spatrick 160*e5dd7070Spatrick NEW_RECEIVER(UITabBarItem) 161*e5dd7070Spatrick IdentifierInfo *initWithTitleUITabBarItemTag[] = { 162*e5dd7070Spatrick &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("image"), 163*e5dd7070Spatrick &Ctx.Idents.get("tag")}; 164*e5dd7070Spatrick ADD_METHOD(UITabBarItem, initWithTitleUITabBarItemTag, 3, 0) 165*e5dd7070Spatrick IdentifierInfo *initWithTitleUITabBarItemImage[] = { 166*e5dd7070Spatrick &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("image"), 167*e5dd7070Spatrick &Ctx.Idents.get("selectedImage")}; 168*e5dd7070Spatrick ADD_METHOD(UITabBarItem, initWithTitleUITabBarItemImage, 3, 0) 169*e5dd7070Spatrick 170*e5dd7070Spatrick NEW_RECEIVER(NSDockTile) 171*e5dd7070Spatrick ADD_UNARY_METHOD(NSDockTile, setBadgeLabel, 0) 172*e5dd7070Spatrick 173*e5dd7070Spatrick NEW_RECEIVER(NSStatusItem) 174*e5dd7070Spatrick ADD_UNARY_METHOD(NSStatusItem, setTitle, 0) 175*e5dd7070Spatrick ADD_UNARY_METHOD(NSStatusItem, setToolTip, 0) 176*e5dd7070Spatrick 177*e5dd7070Spatrick NEW_RECEIVER(UITableViewRowAction) 178*e5dd7070Spatrick IdentifierInfo *rowActionWithStyleUITableViewRowAction[] = { 179*e5dd7070Spatrick &Ctx.Idents.get("rowActionWithStyle"), &Ctx.Idents.get("title"), 180*e5dd7070Spatrick &Ctx.Idents.get("handler")}; 181*e5dd7070Spatrick ADD_METHOD(UITableViewRowAction, rowActionWithStyleUITableViewRowAction, 3, 1) 182*e5dd7070Spatrick ADD_UNARY_METHOD(UITableViewRowAction, setTitle, 0) 183*e5dd7070Spatrick 184*e5dd7070Spatrick NEW_RECEIVER(NSBox) 185*e5dd7070Spatrick ADD_UNARY_METHOD(NSBox, setTitle, 0) 186*e5dd7070Spatrick 187*e5dd7070Spatrick NEW_RECEIVER(NSButton) 188*e5dd7070Spatrick ADD_UNARY_METHOD(NSButton, setTitle, 0) 189*e5dd7070Spatrick ADD_UNARY_METHOD(NSButton, setAlternateTitle, 0) 190*e5dd7070Spatrick IdentifierInfo *radioButtonWithTitleNSButton[] = { 191*e5dd7070Spatrick &Ctx.Idents.get("radioButtonWithTitle"), &Ctx.Idents.get("target"), 192*e5dd7070Spatrick &Ctx.Idents.get("action")}; 193*e5dd7070Spatrick ADD_METHOD(NSButton, radioButtonWithTitleNSButton, 3, 0) 194*e5dd7070Spatrick IdentifierInfo *buttonWithTitleNSButtonImage[] = { 195*e5dd7070Spatrick &Ctx.Idents.get("buttonWithTitle"), &Ctx.Idents.get("image"), 196*e5dd7070Spatrick &Ctx.Idents.get("target"), &Ctx.Idents.get("action")}; 197*e5dd7070Spatrick ADD_METHOD(NSButton, buttonWithTitleNSButtonImage, 4, 0) 198*e5dd7070Spatrick IdentifierInfo *checkboxWithTitleNSButton[] = { 199*e5dd7070Spatrick &Ctx.Idents.get("checkboxWithTitle"), &Ctx.Idents.get("target"), 200*e5dd7070Spatrick &Ctx.Idents.get("action")}; 201*e5dd7070Spatrick ADD_METHOD(NSButton, checkboxWithTitleNSButton, 3, 0) 202*e5dd7070Spatrick IdentifierInfo *buttonWithTitleNSButtonTarget[] = { 203*e5dd7070Spatrick &Ctx.Idents.get("buttonWithTitle"), &Ctx.Idents.get("target"), 204*e5dd7070Spatrick &Ctx.Idents.get("action")}; 205*e5dd7070Spatrick ADD_METHOD(NSButton, buttonWithTitleNSButtonTarget, 3, 0) 206*e5dd7070Spatrick 207*e5dd7070Spatrick NEW_RECEIVER(NSSavePanel) 208*e5dd7070Spatrick ADD_UNARY_METHOD(NSSavePanel, setPrompt, 0) 209*e5dd7070Spatrick ADD_UNARY_METHOD(NSSavePanel, setTitle, 0) 210*e5dd7070Spatrick ADD_UNARY_METHOD(NSSavePanel, setNameFieldLabel, 0) 211*e5dd7070Spatrick ADD_UNARY_METHOD(NSSavePanel, setNameFieldStringValue, 0) 212*e5dd7070Spatrick ADD_UNARY_METHOD(NSSavePanel, setMessage, 0) 213*e5dd7070Spatrick 214*e5dd7070Spatrick NEW_RECEIVER(UIPrintInfo) 215*e5dd7070Spatrick ADD_UNARY_METHOD(UIPrintInfo, setJobName, 0) 216*e5dd7070Spatrick 217*e5dd7070Spatrick NEW_RECEIVER(NSTabViewItem) 218*e5dd7070Spatrick ADD_UNARY_METHOD(NSTabViewItem, setLabel, 0) 219*e5dd7070Spatrick ADD_UNARY_METHOD(NSTabViewItem, setToolTip, 0) 220*e5dd7070Spatrick 221*e5dd7070Spatrick NEW_RECEIVER(NSBrowser) 222*e5dd7070Spatrick IdentifierInfo *setTitleNSBrowser[] = {&Ctx.Idents.get("setTitle"), 223*e5dd7070Spatrick &Ctx.Idents.get("ofColumn")}; 224*e5dd7070Spatrick ADD_METHOD(NSBrowser, setTitleNSBrowser, 2, 0) 225*e5dd7070Spatrick 226*e5dd7070Spatrick NEW_RECEIVER(UIAccessibilityElement) 227*e5dd7070Spatrick ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityLabel, 0) 228*e5dd7070Spatrick ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityHint, 0) 229*e5dd7070Spatrick ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityValue, 0) 230*e5dd7070Spatrick 231*e5dd7070Spatrick NEW_RECEIVER(UIAlertAction) 232*e5dd7070Spatrick IdentifierInfo *actionWithTitleUIAlertAction[] = { 233*e5dd7070Spatrick &Ctx.Idents.get("actionWithTitle"), &Ctx.Idents.get("style"), 234*e5dd7070Spatrick &Ctx.Idents.get("handler")}; 235*e5dd7070Spatrick ADD_METHOD(UIAlertAction, actionWithTitleUIAlertAction, 3, 0) 236*e5dd7070Spatrick 237*e5dd7070Spatrick NEW_RECEIVER(NSPopUpButton) 238*e5dd7070Spatrick ADD_UNARY_METHOD(NSPopUpButton, addItemWithTitle, 0) 239*e5dd7070Spatrick IdentifierInfo *insertItemWithTitleNSPopUpButton[] = { 240*e5dd7070Spatrick &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("atIndex")}; 241*e5dd7070Spatrick ADD_METHOD(NSPopUpButton, insertItemWithTitleNSPopUpButton, 2, 0) 242*e5dd7070Spatrick ADD_UNARY_METHOD(NSPopUpButton, removeItemWithTitle, 0) 243*e5dd7070Spatrick ADD_UNARY_METHOD(NSPopUpButton, selectItemWithTitle, 0) 244*e5dd7070Spatrick ADD_UNARY_METHOD(NSPopUpButton, setTitle, 0) 245*e5dd7070Spatrick 246*e5dd7070Spatrick NEW_RECEIVER(NSTableViewRowAction) 247*e5dd7070Spatrick IdentifierInfo *rowActionWithStyleNSTableViewRowAction[] = { 248*e5dd7070Spatrick &Ctx.Idents.get("rowActionWithStyle"), &Ctx.Idents.get("title"), 249*e5dd7070Spatrick &Ctx.Idents.get("handler")}; 250*e5dd7070Spatrick ADD_METHOD(NSTableViewRowAction, rowActionWithStyleNSTableViewRowAction, 3, 1) 251*e5dd7070Spatrick ADD_UNARY_METHOD(NSTableViewRowAction, setTitle, 0) 252*e5dd7070Spatrick 253*e5dd7070Spatrick NEW_RECEIVER(NSImage) 254*e5dd7070Spatrick ADD_UNARY_METHOD(NSImage, setAccessibilityDescription, 0) 255*e5dd7070Spatrick 256*e5dd7070Spatrick NEW_RECEIVER(NSUserActivity) 257*e5dd7070Spatrick ADD_UNARY_METHOD(NSUserActivity, setTitle, 0) 258*e5dd7070Spatrick 259*e5dd7070Spatrick NEW_RECEIVER(NSPathControlItem) 260*e5dd7070Spatrick ADD_UNARY_METHOD(NSPathControlItem, setTitle, 0) 261*e5dd7070Spatrick 262*e5dd7070Spatrick NEW_RECEIVER(NSCell) 263*e5dd7070Spatrick ADD_UNARY_METHOD(NSCell, initTextCell, 0) 264*e5dd7070Spatrick ADD_UNARY_METHOD(NSCell, setTitle, 0) 265*e5dd7070Spatrick ADD_UNARY_METHOD(NSCell, setStringValue, 0) 266*e5dd7070Spatrick 267*e5dd7070Spatrick NEW_RECEIVER(NSPathControl) 268*e5dd7070Spatrick ADD_UNARY_METHOD(NSPathControl, setPlaceholderString, 0) 269*e5dd7070Spatrick 270*e5dd7070Spatrick NEW_RECEIVER(UIAccessibility) 271*e5dd7070Spatrick ADD_UNARY_METHOD(UIAccessibility, setAccessibilityLabel, 0) 272*e5dd7070Spatrick ADD_UNARY_METHOD(UIAccessibility, setAccessibilityHint, 0) 273*e5dd7070Spatrick ADD_UNARY_METHOD(UIAccessibility, setAccessibilityValue, 0) 274*e5dd7070Spatrick 275*e5dd7070Spatrick NEW_RECEIVER(NSTableColumn) 276*e5dd7070Spatrick ADD_UNARY_METHOD(NSTableColumn, setTitle, 0) 277*e5dd7070Spatrick ADD_UNARY_METHOD(NSTableColumn, setHeaderToolTip, 0) 278*e5dd7070Spatrick 279*e5dd7070Spatrick NEW_RECEIVER(NSSegmentedControl) 280*e5dd7070Spatrick IdentifierInfo *setLabelNSSegmentedControl[] = { 281*e5dd7070Spatrick &Ctx.Idents.get("setLabel"), &Ctx.Idents.get("forSegment")}; 282*e5dd7070Spatrick ADD_METHOD(NSSegmentedControl, setLabelNSSegmentedControl, 2, 0) 283*e5dd7070Spatrick IdentifierInfo *setToolTipNSSegmentedControl[] = { 284*e5dd7070Spatrick &Ctx.Idents.get("setToolTip"), &Ctx.Idents.get("forSegment")}; 285*e5dd7070Spatrick ADD_METHOD(NSSegmentedControl, setToolTipNSSegmentedControl, 2, 0) 286*e5dd7070Spatrick 287*e5dd7070Spatrick NEW_RECEIVER(NSButtonCell) 288*e5dd7070Spatrick ADD_UNARY_METHOD(NSButtonCell, setTitle, 0) 289*e5dd7070Spatrick ADD_UNARY_METHOD(NSButtonCell, setAlternateTitle, 0) 290*e5dd7070Spatrick 291*e5dd7070Spatrick NEW_RECEIVER(NSDatePickerCell) 292*e5dd7070Spatrick ADD_UNARY_METHOD(NSDatePickerCell, initTextCell, 0) 293*e5dd7070Spatrick 294*e5dd7070Spatrick NEW_RECEIVER(NSSliderCell) 295*e5dd7070Spatrick ADD_UNARY_METHOD(NSSliderCell, setTitle, 0) 296*e5dd7070Spatrick 297*e5dd7070Spatrick NEW_RECEIVER(NSControl) 298*e5dd7070Spatrick ADD_UNARY_METHOD(NSControl, setStringValue, 0) 299*e5dd7070Spatrick 300*e5dd7070Spatrick NEW_RECEIVER(NSAccessibility) 301*e5dd7070Spatrick ADD_UNARY_METHOD(NSAccessibility, setAccessibilityValueDescription, 0) 302*e5dd7070Spatrick ADD_UNARY_METHOD(NSAccessibility, setAccessibilityLabel, 0) 303*e5dd7070Spatrick ADD_UNARY_METHOD(NSAccessibility, setAccessibilityTitle, 0) 304*e5dd7070Spatrick ADD_UNARY_METHOD(NSAccessibility, setAccessibilityPlaceholderValue, 0) 305*e5dd7070Spatrick ADD_UNARY_METHOD(NSAccessibility, setAccessibilityHelp, 0) 306*e5dd7070Spatrick 307*e5dd7070Spatrick NEW_RECEIVER(NSMatrix) 308*e5dd7070Spatrick IdentifierInfo *setToolTipNSMatrix[] = {&Ctx.Idents.get("setToolTip"), 309*e5dd7070Spatrick &Ctx.Idents.get("forCell")}; 310*e5dd7070Spatrick ADD_METHOD(NSMatrix, setToolTipNSMatrix, 2, 0) 311*e5dd7070Spatrick 312*e5dd7070Spatrick NEW_RECEIVER(NSPrintPanel) 313*e5dd7070Spatrick ADD_UNARY_METHOD(NSPrintPanel, setDefaultButtonTitle, 0) 314*e5dd7070Spatrick 315*e5dd7070Spatrick NEW_RECEIVER(UILocalNotification) 316*e5dd7070Spatrick ADD_UNARY_METHOD(UILocalNotification, setAlertBody, 0) 317*e5dd7070Spatrick ADD_UNARY_METHOD(UILocalNotification, setAlertAction, 0) 318*e5dd7070Spatrick ADD_UNARY_METHOD(UILocalNotification, setAlertTitle, 0) 319*e5dd7070Spatrick 320*e5dd7070Spatrick NEW_RECEIVER(NSSlider) 321*e5dd7070Spatrick ADD_UNARY_METHOD(NSSlider, setTitle, 0) 322*e5dd7070Spatrick 323*e5dd7070Spatrick NEW_RECEIVER(UIMenuItem) 324*e5dd7070Spatrick IdentifierInfo *initWithTitleUIMenuItem[] = {&Ctx.Idents.get("initWithTitle"), 325*e5dd7070Spatrick &Ctx.Idents.get("action")}; 326*e5dd7070Spatrick ADD_METHOD(UIMenuItem, initWithTitleUIMenuItem, 2, 0) 327*e5dd7070Spatrick ADD_UNARY_METHOD(UIMenuItem, setTitle, 0) 328*e5dd7070Spatrick 329*e5dd7070Spatrick NEW_RECEIVER(UIAlertController) 330*e5dd7070Spatrick IdentifierInfo *alertControllerWithTitleUIAlertController[] = { 331*e5dd7070Spatrick &Ctx.Idents.get("alertControllerWithTitle"), &Ctx.Idents.get("message"), 332*e5dd7070Spatrick &Ctx.Idents.get("preferredStyle")}; 333*e5dd7070Spatrick ADD_METHOD(UIAlertController, alertControllerWithTitleUIAlertController, 3, 1) 334*e5dd7070Spatrick ADD_UNARY_METHOD(UIAlertController, setTitle, 0) 335*e5dd7070Spatrick ADD_UNARY_METHOD(UIAlertController, setMessage, 0) 336*e5dd7070Spatrick 337*e5dd7070Spatrick NEW_RECEIVER(UIApplicationShortcutItem) 338*e5dd7070Spatrick IdentifierInfo *initWithTypeUIApplicationShortcutItemIcon[] = { 339*e5dd7070Spatrick &Ctx.Idents.get("initWithType"), &Ctx.Idents.get("localizedTitle"), 340*e5dd7070Spatrick &Ctx.Idents.get("localizedSubtitle"), &Ctx.Idents.get("icon"), 341*e5dd7070Spatrick &Ctx.Idents.get("userInfo")}; 342*e5dd7070Spatrick ADD_METHOD(UIApplicationShortcutItem, 343*e5dd7070Spatrick initWithTypeUIApplicationShortcutItemIcon, 5, 1) 344*e5dd7070Spatrick IdentifierInfo *initWithTypeUIApplicationShortcutItem[] = { 345*e5dd7070Spatrick &Ctx.Idents.get("initWithType"), &Ctx.Idents.get("localizedTitle")}; 346*e5dd7070Spatrick ADD_METHOD(UIApplicationShortcutItem, initWithTypeUIApplicationShortcutItem, 347*e5dd7070Spatrick 2, 1) 348*e5dd7070Spatrick 349*e5dd7070Spatrick NEW_RECEIVER(UIActionSheet) 350*e5dd7070Spatrick IdentifierInfo *initWithTitleUIActionSheet[] = { 351*e5dd7070Spatrick &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("delegate"), 352*e5dd7070Spatrick &Ctx.Idents.get("cancelButtonTitle"), 353*e5dd7070Spatrick &Ctx.Idents.get("destructiveButtonTitle"), 354*e5dd7070Spatrick &Ctx.Idents.get("otherButtonTitles")}; 355*e5dd7070Spatrick ADD_METHOD(UIActionSheet, initWithTitleUIActionSheet, 5, 0) 356*e5dd7070Spatrick ADD_UNARY_METHOD(UIActionSheet, addButtonWithTitle, 0) 357*e5dd7070Spatrick ADD_UNARY_METHOD(UIActionSheet, setTitle, 0) 358*e5dd7070Spatrick 359*e5dd7070Spatrick NEW_RECEIVER(UIAccessibilityCustomAction) 360*e5dd7070Spatrick IdentifierInfo *initWithNameUIAccessibilityCustomAction[] = { 361*e5dd7070Spatrick &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("target"), 362*e5dd7070Spatrick &Ctx.Idents.get("selector")}; 363*e5dd7070Spatrick ADD_METHOD(UIAccessibilityCustomAction, 364*e5dd7070Spatrick initWithNameUIAccessibilityCustomAction, 3, 0) 365*e5dd7070Spatrick ADD_UNARY_METHOD(UIAccessibilityCustomAction, setName, 0) 366*e5dd7070Spatrick 367*e5dd7070Spatrick NEW_RECEIVER(UISearchBar) 368*e5dd7070Spatrick ADD_UNARY_METHOD(UISearchBar, setText, 0) 369*e5dd7070Spatrick ADD_UNARY_METHOD(UISearchBar, setPrompt, 0) 370*e5dd7070Spatrick ADD_UNARY_METHOD(UISearchBar, setPlaceholder, 0) 371*e5dd7070Spatrick 372*e5dd7070Spatrick NEW_RECEIVER(UIBarItem) 373*e5dd7070Spatrick ADD_UNARY_METHOD(UIBarItem, setTitle, 0) 374*e5dd7070Spatrick 375*e5dd7070Spatrick NEW_RECEIVER(UITextView) 376*e5dd7070Spatrick ADD_UNARY_METHOD(UITextView, setText, 0) 377*e5dd7070Spatrick 378*e5dd7070Spatrick NEW_RECEIVER(NSView) 379*e5dd7070Spatrick ADD_UNARY_METHOD(NSView, setToolTip, 0) 380*e5dd7070Spatrick 381*e5dd7070Spatrick NEW_RECEIVER(NSTextField) 382*e5dd7070Spatrick ADD_UNARY_METHOD(NSTextField, setPlaceholderString, 0) 383*e5dd7070Spatrick ADD_UNARY_METHOD(NSTextField, textFieldWithString, 0) 384*e5dd7070Spatrick ADD_UNARY_METHOD(NSTextField, wrappingLabelWithString, 0) 385*e5dd7070Spatrick ADD_UNARY_METHOD(NSTextField, labelWithString, 0) 386*e5dd7070Spatrick 387*e5dd7070Spatrick NEW_RECEIVER(NSAttributedString) 388*e5dd7070Spatrick ADD_UNARY_METHOD(NSAttributedString, initWithString, 0) 389*e5dd7070Spatrick IdentifierInfo *initWithStringNSAttributedString[] = { 390*e5dd7070Spatrick &Ctx.Idents.get("initWithString"), &Ctx.Idents.get("attributes")}; 391*e5dd7070Spatrick ADD_METHOD(NSAttributedString, initWithStringNSAttributedString, 2, 0) 392*e5dd7070Spatrick 393*e5dd7070Spatrick NEW_RECEIVER(NSText) 394*e5dd7070Spatrick ADD_UNARY_METHOD(NSText, setString, 0) 395*e5dd7070Spatrick 396*e5dd7070Spatrick NEW_RECEIVER(UIKeyCommand) 397*e5dd7070Spatrick IdentifierInfo *keyCommandWithInputUIKeyCommand[] = { 398*e5dd7070Spatrick &Ctx.Idents.get("keyCommandWithInput"), &Ctx.Idents.get("modifierFlags"), 399*e5dd7070Spatrick &Ctx.Idents.get("action"), &Ctx.Idents.get("discoverabilityTitle")}; 400*e5dd7070Spatrick ADD_METHOD(UIKeyCommand, keyCommandWithInputUIKeyCommand, 4, 3) 401*e5dd7070Spatrick ADD_UNARY_METHOD(UIKeyCommand, setDiscoverabilityTitle, 0) 402*e5dd7070Spatrick 403*e5dd7070Spatrick NEW_RECEIVER(UILabel) 404*e5dd7070Spatrick ADD_UNARY_METHOD(UILabel, setText, 0) 405*e5dd7070Spatrick 406*e5dd7070Spatrick NEW_RECEIVER(NSAlert) 407*e5dd7070Spatrick IdentifierInfo *alertWithMessageTextNSAlert[] = { 408*e5dd7070Spatrick &Ctx.Idents.get("alertWithMessageText"), &Ctx.Idents.get("defaultButton"), 409*e5dd7070Spatrick &Ctx.Idents.get("alternateButton"), &Ctx.Idents.get("otherButton"), 410*e5dd7070Spatrick &Ctx.Idents.get("informativeTextWithFormat")}; 411*e5dd7070Spatrick ADD_METHOD(NSAlert, alertWithMessageTextNSAlert, 5, 0) 412*e5dd7070Spatrick ADD_UNARY_METHOD(NSAlert, addButtonWithTitle, 0) 413*e5dd7070Spatrick ADD_UNARY_METHOD(NSAlert, setMessageText, 0) 414*e5dd7070Spatrick ADD_UNARY_METHOD(NSAlert, setInformativeText, 0) 415*e5dd7070Spatrick ADD_UNARY_METHOD(NSAlert, setHelpAnchor, 0) 416*e5dd7070Spatrick 417*e5dd7070Spatrick NEW_RECEIVER(UIMutableApplicationShortcutItem) 418*e5dd7070Spatrick ADD_UNARY_METHOD(UIMutableApplicationShortcutItem, setLocalizedTitle, 0) 419*e5dd7070Spatrick ADD_UNARY_METHOD(UIMutableApplicationShortcutItem, setLocalizedSubtitle, 0) 420*e5dd7070Spatrick 421*e5dd7070Spatrick NEW_RECEIVER(UIButton) 422*e5dd7070Spatrick IdentifierInfo *setTitleUIButton[] = {&Ctx.Idents.get("setTitle"), 423*e5dd7070Spatrick &Ctx.Idents.get("forState")}; 424*e5dd7070Spatrick ADD_METHOD(UIButton, setTitleUIButton, 2, 0) 425*e5dd7070Spatrick 426*e5dd7070Spatrick NEW_RECEIVER(NSWindow) 427*e5dd7070Spatrick ADD_UNARY_METHOD(NSWindow, setTitle, 0) 428*e5dd7070Spatrick IdentifierInfo *minFrameWidthWithTitleNSWindow[] = { 429*e5dd7070Spatrick &Ctx.Idents.get("minFrameWidthWithTitle"), &Ctx.Idents.get("styleMask")}; 430*e5dd7070Spatrick ADD_METHOD(NSWindow, minFrameWidthWithTitleNSWindow, 2, 0) 431*e5dd7070Spatrick ADD_UNARY_METHOD(NSWindow, setMiniwindowTitle, 0) 432*e5dd7070Spatrick 433*e5dd7070Spatrick NEW_RECEIVER(NSPathCell) 434*e5dd7070Spatrick ADD_UNARY_METHOD(NSPathCell, setPlaceholderString, 0) 435*e5dd7070Spatrick 436*e5dd7070Spatrick NEW_RECEIVER(UIDocumentMenuViewController) 437*e5dd7070Spatrick IdentifierInfo *addOptionWithTitleUIDocumentMenuViewController[] = { 438*e5dd7070Spatrick &Ctx.Idents.get("addOptionWithTitle"), &Ctx.Idents.get("image"), 439*e5dd7070Spatrick &Ctx.Idents.get("order"), &Ctx.Idents.get("handler")}; 440*e5dd7070Spatrick ADD_METHOD(UIDocumentMenuViewController, 441*e5dd7070Spatrick addOptionWithTitleUIDocumentMenuViewController, 4, 0) 442*e5dd7070Spatrick 443*e5dd7070Spatrick NEW_RECEIVER(UINavigationItem) 444*e5dd7070Spatrick ADD_UNARY_METHOD(UINavigationItem, initWithTitle, 0) 445*e5dd7070Spatrick ADD_UNARY_METHOD(UINavigationItem, setTitle, 0) 446*e5dd7070Spatrick ADD_UNARY_METHOD(UINavigationItem, setPrompt, 0) 447*e5dd7070Spatrick 448*e5dd7070Spatrick NEW_RECEIVER(UIAlertView) 449*e5dd7070Spatrick IdentifierInfo *initWithTitleUIAlertView[] = { 450*e5dd7070Spatrick &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("message"), 451*e5dd7070Spatrick &Ctx.Idents.get("delegate"), &Ctx.Idents.get("cancelButtonTitle"), 452*e5dd7070Spatrick &Ctx.Idents.get("otherButtonTitles")}; 453*e5dd7070Spatrick ADD_METHOD(UIAlertView, initWithTitleUIAlertView, 5, 0) 454*e5dd7070Spatrick ADD_UNARY_METHOD(UIAlertView, addButtonWithTitle, 0) 455*e5dd7070Spatrick ADD_UNARY_METHOD(UIAlertView, setTitle, 0) 456*e5dd7070Spatrick ADD_UNARY_METHOD(UIAlertView, setMessage, 0) 457*e5dd7070Spatrick 458*e5dd7070Spatrick NEW_RECEIVER(NSFormCell) 459*e5dd7070Spatrick ADD_UNARY_METHOD(NSFormCell, initTextCell, 0) 460*e5dd7070Spatrick ADD_UNARY_METHOD(NSFormCell, setTitle, 0) 461*e5dd7070Spatrick ADD_UNARY_METHOD(NSFormCell, setPlaceholderString, 0) 462*e5dd7070Spatrick 463*e5dd7070Spatrick NEW_RECEIVER(NSUserNotification) 464*e5dd7070Spatrick ADD_UNARY_METHOD(NSUserNotification, setTitle, 0) 465*e5dd7070Spatrick ADD_UNARY_METHOD(NSUserNotification, setSubtitle, 0) 466*e5dd7070Spatrick ADD_UNARY_METHOD(NSUserNotification, setInformativeText, 0) 467*e5dd7070Spatrick ADD_UNARY_METHOD(NSUserNotification, setActionButtonTitle, 0) 468*e5dd7070Spatrick ADD_UNARY_METHOD(NSUserNotification, setOtherButtonTitle, 0) 469*e5dd7070Spatrick ADD_UNARY_METHOD(NSUserNotification, setResponsePlaceholder, 0) 470*e5dd7070Spatrick 471*e5dd7070Spatrick NEW_RECEIVER(NSToolbarItem) 472*e5dd7070Spatrick ADD_UNARY_METHOD(NSToolbarItem, setLabel, 0) 473*e5dd7070Spatrick ADD_UNARY_METHOD(NSToolbarItem, setPaletteLabel, 0) 474*e5dd7070Spatrick ADD_UNARY_METHOD(NSToolbarItem, setToolTip, 0) 475*e5dd7070Spatrick 476*e5dd7070Spatrick NEW_RECEIVER(NSProgress) 477*e5dd7070Spatrick ADD_UNARY_METHOD(NSProgress, setLocalizedDescription, 0) 478*e5dd7070Spatrick ADD_UNARY_METHOD(NSProgress, setLocalizedAdditionalDescription, 0) 479*e5dd7070Spatrick 480*e5dd7070Spatrick NEW_RECEIVER(NSSegmentedCell) 481*e5dd7070Spatrick IdentifierInfo *setLabelNSSegmentedCell[] = {&Ctx.Idents.get("setLabel"), 482*e5dd7070Spatrick &Ctx.Idents.get("forSegment")}; 483*e5dd7070Spatrick ADD_METHOD(NSSegmentedCell, setLabelNSSegmentedCell, 2, 0) 484*e5dd7070Spatrick IdentifierInfo *setToolTipNSSegmentedCell[] = {&Ctx.Idents.get("setToolTip"), 485*e5dd7070Spatrick &Ctx.Idents.get("forSegment")}; 486*e5dd7070Spatrick ADD_METHOD(NSSegmentedCell, setToolTipNSSegmentedCell, 2, 0) 487*e5dd7070Spatrick 488*e5dd7070Spatrick NEW_RECEIVER(NSUndoManager) 489*e5dd7070Spatrick ADD_UNARY_METHOD(NSUndoManager, setActionName, 0) 490*e5dd7070Spatrick ADD_UNARY_METHOD(NSUndoManager, undoMenuTitleForUndoActionName, 0) 491*e5dd7070Spatrick ADD_UNARY_METHOD(NSUndoManager, redoMenuTitleForUndoActionName, 0) 492*e5dd7070Spatrick 493*e5dd7070Spatrick NEW_RECEIVER(NSMenuItem) 494*e5dd7070Spatrick IdentifierInfo *initWithTitleNSMenuItem[] = { 495*e5dd7070Spatrick &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("action"), 496*e5dd7070Spatrick &Ctx.Idents.get("keyEquivalent")}; 497*e5dd7070Spatrick ADD_METHOD(NSMenuItem, initWithTitleNSMenuItem, 3, 0) 498*e5dd7070Spatrick ADD_UNARY_METHOD(NSMenuItem, setTitle, 0) 499*e5dd7070Spatrick ADD_UNARY_METHOD(NSMenuItem, setToolTip, 0) 500*e5dd7070Spatrick 501*e5dd7070Spatrick NEW_RECEIVER(NSPopUpButtonCell) 502*e5dd7070Spatrick IdentifierInfo *initTextCellNSPopUpButtonCell[] = { 503*e5dd7070Spatrick &Ctx.Idents.get("initTextCell"), &Ctx.Idents.get("pullsDown")}; 504*e5dd7070Spatrick ADD_METHOD(NSPopUpButtonCell, initTextCellNSPopUpButtonCell, 2, 0) 505*e5dd7070Spatrick ADD_UNARY_METHOD(NSPopUpButtonCell, addItemWithTitle, 0) 506*e5dd7070Spatrick IdentifierInfo *insertItemWithTitleNSPopUpButtonCell[] = { 507*e5dd7070Spatrick &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("atIndex")}; 508*e5dd7070Spatrick ADD_METHOD(NSPopUpButtonCell, insertItemWithTitleNSPopUpButtonCell, 2, 0) 509*e5dd7070Spatrick ADD_UNARY_METHOD(NSPopUpButtonCell, removeItemWithTitle, 0) 510*e5dd7070Spatrick ADD_UNARY_METHOD(NSPopUpButtonCell, selectItemWithTitle, 0) 511*e5dd7070Spatrick ADD_UNARY_METHOD(NSPopUpButtonCell, setTitle, 0) 512*e5dd7070Spatrick 513*e5dd7070Spatrick NEW_RECEIVER(NSViewController) 514*e5dd7070Spatrick ADD_UNARY_METHOD(NSViewController, setTitle, 0) 515*e5dd7070Spatrick 516*e5dd7070Spatrick NEW_RECEIVER(NSMenu) 517*e5dd7070Spatrick ADD_UNARY_METHOD(NSMenu, initWithTitle, 0) 518*e5dd7070Spatrick IdentifierInfo *insertItemWithTitleNSMenu[] = { 519*e5dd7070Spatrick &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("action"), 520*e5dd7070Spatrick &Ctx.Idents.get("keyEquivalent"), &Ctx.Idents.get("atIndex")}; 521*e5dd7070Spatrick ADD_METHOD(NSMenu, insertItemWithTitleNSMenu, 4, 0) 522*e5dd7070Spatrick IdentifierInfo *addItemWithTitleNSMenu[] = { 523*e5dd7070Spatrick &Ctx.Idents.get("addItemWithTitle"), &Ctx.Idents.get("action"), 524*e5dd7070Spatrick &Ctx.Idents.get("keyEquivalent")}; 525*e5dd7070Spatrick ADD_METHOD(NSMenu, addItemWithTitleNSMenu, 3, 0) 526*e5dd7070Spatrick ADD_UNARY_METHOD(NSMenu, setTitle, 0) 527*e5dd7070Spatrick 528*e5dd7070Spatrick NEW_RECEIVER(UIMutableUserNotificationAction) 529*e5dd7070Spatrick ADD_UNARY_METHOD(UIMutableUserNotificationAction, setTitle, 0) 530*e5dd7070Spatrick 531*e5dd7070Spatrick NEW_RECEIVER(NSForm) 532*e5dd7070Spatrick ADD_UNARY_METHOD(NSForm, addEntry, 0) 533*e5dd7070Spatrick IdentifierInfo *insertEntryNSForm[] = {&Ctx.Idents.get("insertEntry"), 534*e5dd7070Spatrick &Ctx.Idents.get("atIndex")}; 535*e5dd7070Spatrick ADD_METHOD(NSForm, insertEntryNSForm, 2, 0) 536*e5dd7070Spatrick 537*e5dd7070Spatrick NEW_RECEIVER(NSTextFieldCell) 538*e5dd7070Spatrick ADD_UNARY_METHOD(NSTextFieldCell, setPlaceholderString, 0) 539*e5dd7070Spatrick 540*e5dd7070Spatrick NEW_RECEIVER(NSUserNotificationAction) 541*e5dd7070Spatrick IdentifierInfo *actionWithIdentifierNSUserNotificationAction[] = { 542*e5dd7070Spatrick &Ctx.Idents.get("actionWithIdentifier"), &Ctx.Idents.get("title")}; 543*e5dd7070Spatrick ADD_METHOD(NSUserNotificationAction, 544*e5dd7070Spatrick actionWithIdentifierNSUserNotificationAction, 2, 1) 545*e5dd7070Spatrick 546*e5dd7070Spatrick NEW_RECEIVER(UITextField) 547*e5dd7070Spatrick ADD_UNARY_METHOD(UITextField, setText, 0) 548*e5dd7070Spatrick ADD_UNARY_METHOD(UITextField, setPlaceholder, 0) 549*e5dd7070Spatrick 550*e5dd7070Spatrick NEW_RECEIVER(UIBarButtonItem) 551*e5dd7070Spatrick IdentifierInfo *initWithTitleUIBarButtonItem[] = { 552*e5dd7070Spatrick &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("style"), 553*e5dd7070Spatrick &Ctx.Idents.get("target"), &Ctx.Idents.get("action")}; 554*e5dd7070Spatrick ADD_METHOD(UIBarButtonItem, initWithTitleUIBarButtonItem, 4, 0) 555*e5dd7070Spatrick 556*e5dd7070Spatrick NEW_RECEIVER(UIViewController) 557*e5dd7070Spatrick ADD_UNARY_METHOD(UIViewController, setTitle, 0) 558*e5dd7070Spatrick 559*e5dd7070Spatrick NEW_RECEIVER(UISegmentedControl) 560*e5dd7070Spatrick IdentifierInfo *insertSegmentWithTitleUISegmentedControl[] = { 561*e5dd7070Spatrick &Ctx.Idents.get("insertSegmentWithTitle"), &Ctx.Idents.get("atIndex"), 562*e5dd7070Spatrick &Ctx.Idents.get("animated")}; 563*e5dd7070Spatrick ADD_METHOD(UISegmentedControl, insertSegmentWithTitleUISegmentedControl, 3, 0) 564*e5dd7070Spatrick IdentifierInfo *setTitleUISegmentedControl[] = { 565*e5dd7070Spatrick &Ctx.Idents.get("setTitle"), &Ctx.Idents.get("forSegmentAtIndex")}; 566*e5dd7070Spatrick ADD_METHOD(UISegmentedControl, setTitleUISegmentedControl, 2, 0) 567*e5dd7070Spatrick 568*e5dd7070Spatrick NEW_RECEIVER(NSAccessibilityCustomRotorItemResult) 569*e5dd7070Spatrick IdentifierInfo 570*e5dd7070Spatrick *initWithItemLoadingTokenNSAccessibilityCustomRotorItemResult[] = { 571*e5dd7070Spatrick &Ctx.Idents.get("initWithItemLoadingToken"), 572*e5dd7070Spatrick &Ctx.Idents.get("customLabel")}; 573*e5dd7070Spatrick ADD_METHOD(NSAccessibilityCustomRotorItemResult, 574*e5dd7070Spatrick initWithItemLoadingTokenNSAccessibilityCustomRotorItemResult, 2, 1) 575*e5dd7070Spatrick ADD_UNARY_METHOD(NSAccessibilityCustomRotorItemResult, setCustomLabel, 0) 576*e5dd7070Spatrick 577*e5dd7070Spatrick NEW_RECEIVER(UIContextualAction) 578*e5dd7070Spatrick IdentifierInfo *contextualActionWithStyleUIContextualAction[] = { 579*e5dd7070Spatrick &Ctx.Idents.get("contextualActionWithStyle"), &Ctx.Idents.get("title"), 580*e5dd7070Spatrick &Ctx.Idents.get("handler")}; 581*e5dd7070Spatrick ADD_METHOD(UIContextualAction, contextualActionWithStyleUIContextualAction, 3, 582*e5dd7070Spatrick 1) 583*e5dd7070Spatrick ADD_UNARY_METHOD(UIContextualAction, setTitle, 0) 584*e5dd7070Spatrick 585*e5dd7070Spatrick NEW_RECEIVER(NSAccessibilityCustomRotor) 586*e5dd7070Spatrick IdentifierInfo *initWithLabelNSAccessibilityCustomRotor[] = { 587*e5dd7070Spatrick &Ctx.Idents.get("initWithLabel"), &Ctx.Idents.get("itemSearchDelegate")}; 588*e5dd7070Spatrick ADD_METHOD(NSAccessibilityCustomRotor, 589*e5dd7070Spatrick initWithLabelNSAccessibilityCustomRotor, 2, 0) 590*e5dd7070Spatrick ADD_UNARY_METHOD(NSAccessibilityCustomRotor, setLabel, 0) 591*e5dd7070Spatrick 592*e5dd7070Spatrick NEW_RECEIVER(NSWindowTab) 593*e5dd7070Spatrick ADD_UNARY_METHOD(NSWindowTab, setTitle, 0) 594*e5dd7070Spatrick ADD_UNARY_METHOD(NSWindowTab, setToolTip, 0) 595*e5dd7070Spatrick 596*e5dd7070Spatrick NEW_RECEIVER(NSAccessibilityCustomAction) 597*e5dd7070Spatrick IdentifierInfo *initWithNameNSAccessibilityCustomAction[] = { 598*e5dd7070Spatrick &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("handler")}; 599*e5dd7070Spatrick ADD_METHOD(NSAccessibilityCustomAction, 600*e5dd7070Spatrick initWithNameNSAccessibilityCustomAction, 2, 0) 601*e5dd7070Spatrick IdentifierInfo *initWithNameTargetNSAccessibilityCustomAction[] = { 602*e5dd7070Spatrick &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("target"), 603*e5dd7070Spatrick &Ctx.Idents.get("selector")}; 604*e5dd7070Spatrick ADD_METHOD(NSAccessibilityCustomAction, 605*e5dd7070Spatrick initWithNameTargetNSAccessibilityCustomAction, 3, 0) 606*e5dd7070Spatrick ADD_UNARY_METHOD(NSAccessibilityCustomAction, setName, 0) 607*e5dd7070Spatrick } 608*e5dd7070Spatrick 609*e5dd7070Spatrick #define LSF_INSERT(function_name) LSF.insert(&Ctx.Idents.get(function_name)); 610*e5dd7070Spatrick #define LSM_INSERT_NULLARY(receiver, method_name) \ 611*e5dd7070Spatrick LSM.insert({&Ctx.Idents.get(receiver), Ctx.Selectors.getNullarySelector( \ 612*e5dd7070Spatrick &Ctx.Idents.get(method_name))}); 613*e5dd7070Spatrick #define LSM_INSERT_UNARY(receiver, method_name) \ 614*e5dd7070Spatrick LSM.insert({&Ctx.Idents.get(receiver), \ 615*e5dd7070Spatrick Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(method_name))}); 616*e5dd7070Spatrick #define LSM_INSERT_SELECTOR(receiver, method_list, arguments) \ 617*e5dd7070Spatrick LSM.insert({&Ctx.Idents.get(receiver), \ 618*e5dd7070Spatrick Ctx.Selectors.getSelector(arguments, method_list)}); 619*e5dd7070Spatrick 620*e5dd7070Spatrick /// Initializes a list of methods and C functions that return a localized string 621*e5dd7070Spatrick void NonLocalizedStringChecker::initLocStringsMethods(ASTContext &Ctx) const { 622*e5dd7070Spatrick if (!LSM.empty()) 623*e5dd7070Spatrick return; 624*e5dd7070Spatrick 625*e5dd7070Spatrick IdentifierInfo *LocalizedStringMacro[] = { 626*e5dd7070Spatrick &Ctx.Idents.get("localizedStringForKey"), &Ctx.Idents.get("value"), 627*e5dd7070Spatrick &Ctx.Idents.get("table")}; 628*e5dd7070Spatrick LSM_INSERT_SELECTOR("NSBundle", LocalizedStringMacro, 3) 629*e5dd7070Spatrick LSM_INSERT_UNARY("NSDateFormatter", "stringFromDate") 630*e5dd7070Spatrick IdentifierInfo *LocalizedStringFromDate[] = { 631*e5dd7070Spatrick &Ctx.Idents.get("localizedStringFromDate"), &Ctx.Idents.get("dateStyle"), 632*e5dd7070Spatrick &Ctx.Idents.get("timeStyle")}; 633*e5dd7070Spatrick LSM_INSERT_SELECTOR("NSDateFormatter", LocalizedStringFromDate, 3) 634*e5dd7070Spatrick LSM_INSERT_UNARY("NSNumberFormatter", "stringFromNumber") 635*e5dd7070Spatrick LSM_INSERT_NULLARY("UITextField", "text") 636*e5dd7070Spatrick LSM_INSERT_NULLARY("UITextView", "text") 637*e5dd7070Spatrick LSM_INSERT_NULLARY("UILabel", "text") 638*e5dd7070Spatrick 639*e5dd7070Spatrick LSF_INSERT("CFDateFormatterCreateStringWithDate"); 640*e5dd7070Spatrick LSF_INSERT("CFDateFormatterCreateStringWithAbsoluteTime"); 641*e5dd7070Spatrick LSF_INSERT("CFNumberFormatterCreateStringWithNumber"); 642*e5dd7070Spatrick } 643*e5dd7070Spatrick 644*e5dd7070Spatrick /// Checks to see if the method / function declaration includes 645*e5dd7070Spatrick /// __attribute__((annotate("returns_localized_nsstring"))) 646*e5dd7070Spatrick bool NonLocalizedStringChecker::isAnnotatedAsReturningLocalized( 647*e5dd7070Spatrick const Decl *D) const { 648*e5dd7070Spatrick if (!D) 649*e5dd7070Spatrick return false; 650*e5dd7070Spatrick return std::any_of( 651*e5dd7070Spatrick D->specific_attr_begin<AnnotateAttr>(), 652*e5dd7070Spatrick D->specific_attr_end<AnnotateAttr>(), [](const AnnotateAttr *Ann) { 653*e5dd7070Spatrick return Ann->getAnnotation() == "returns_localized_nsstring"; 654*e5dd7070Spatrick }); 655*e5dd7070Spatrick } 656*e5dd7070Spatrick 657*e5dd7070Spatrick /// Checks to see if the method / function declaration includes 658*e5dd7070Spatrick /// __attribute__((annotate("takes_localized_nsstring"))) 659*e5dd7070Spatrick bool NonLocalizedStringChecker::isAnnotatedAsTakingLocalized( 660*e5dd7070Spatrick const Decl *D) const { 661*e5dd7070Spatrick if (!D) 662*e5dd7070Spatrick return false; 663*e5dd7070Spatrick return std::any_of( 664*e5dd7070Spatrick D->specific_attr_begin<AnnotateAttr>(), 665*e5dd7070Spatrick D->specific_attr_end<AnnotateAttr>(), [](const AnnotateAttr *Ann) { 666*e5dd7070Spatrick return Ann->getAnnotation() == "takes_localized_nsstring"; 667*e5dd7070Spatrick }); 668*e5dd7070Spatrick } 669*e5dd7070Spatrick 670*e5dd7070Spatrick /// Returns true if the given SVal is marked as Localized in the program state 671*e5dd7070Spatrick bool NonLocalizedStringChecker::hasLocalizedState(SVal S, 672*e5dd7070Spatrick CheckerContext &C) const { 673*e5dd7070Spatrick const MemRegion *mt = S.getAsRegion(); 674*e5dd7070Spatrick if (mt) { 675*e5dd7070Spatrick const LocalizedState *LS = C.getState()->get<LocalizedMemMap>(mt); 676*e5dd7070Spatrick if (LS && LS->isLocalized()) 677*e5dd7070Spatrick return true; 678*e5dd7070Spatrick } 679*e5dd7070Spatrick return false; 680*e5dd7070Spatrick } 681*e5dd7070Spatrick 682*e5dd7070Spatrick /// Returns true if the given SVal is marked as NonLocalized in the program 683*e5dd7070Spatrick /// state 684*e5dd7070Spatrick bool NonLocalizedStringChecker::hasNonLocalizedState(SVal S, 685*e5dd7070Spatrick CheckerContext &C) const { 686*e5dd7070Spatrick const MemRegion *mt = S.getAsRegion(); 687*e5dd7070Spatrick if (mt) { 688*e5dd7070Spatrick const LocalizedState *LS = C.getState()->get<LocalizedMemMap>(mt); 689*e5dd7070Spatrick if (LS && LS->isNonLocalized()) 690*e5dd7070Spatrick return true; 691*e5dd7070Spatrick } 692*e5dd7070Spatrick return false; 693*e5dd7070Spatrick } 694*e5dd7070Spatrick 695*e5dd7070Spatrick /// Marks the given SVal as Localized in the program state 696*e5dd7070Spatrick void NonLocalizedStringChecker::setLocalizedState(const SVal S, 697*e5dd7070Spatrick CheckerContext &C) const { 698*e5dd7070Spatrick const MemRegion *mt = S.getAsRegion(); 699*e5dd7070Spatrick if (mt) { 700*e5dd7070Spatrick ProgramStateRef State = 701*e5dd7070Spatrick C.getState()->set<LocalizedMemMap>(mt, LocalizedState::getLocalized()); 702*e5dd7070Spatrick C.addTransition(State); 703*e5dd7070Spatrick } 704*e5dd7070Spatrick } 705*e5dd7070Spatrick 706*e5dd7070Spatrick /// Marks the given SVal as NonLocalized in the program state 707*e5dd7070Spatrick void NonLocalizedStringChecker::setNonLocalizedState(const SVal S, 708*e5dd7070Spatrick CheckerContext &C) const { 709*e5dd7070Spatrick const MemRegion *mt = S.getAsRegion(); 710*e5dd7070Spatrick if (mt) { 711*e5dd7070Spatrick ProgramStateRef State = C.getState()->set<LocalizedMemMap>( 712*e5dd7070Spatrick mt, LocalizedState::getNonLocalized()); 713*e5dd7070Spatrick C.addTransition(State); 714*e5dd7070Spatrick } 715*e5dd7070Spatrick } 716*e5dd7070Spatrick 717*e5dd7070Spatrick 718*e5dd7070Spatrick static bool isDebuggingName(std::string name) { 719*e5dd7070Spatrick return StringRef(name).lower().find("debug") != StringRef::npos; 720*e5dd7070Spatrick } 721*e5dd7070Spatrick 722*e5dd7070Spatrick /// Returns true when, heuristically, the analyzer may be analyzing debugging 723*e5dd7070Spatrick /// code. We use this to suppress localization diagnostics in un-localized user 724*e5dd7070Spatrick /// interfaces that are only used for debugging and are therefore not user 725*e5dd7070Spatrick /// facing. 726*e5dd7070Spatrick static bool isDebuggingContext(CheckerContext &C) { 727*e5dd7070Spatrick const Decl *D = C.getCurrentAnalysisDeclContext()->getDecl(); 728*e5dd7070Spatrick if (!D) 729*e5dd7070Spatrick return false; 730*e5dd7070Spatrick 731*e5dd7070Spatrick if (auto *ND = dyn_cast<NamedDecl>(D)) { 732*e5dd7070Spatrick if (isDebuggingName(ND->getNameAsString())) 733*e5dd7070Spatrick return true; 734*e5dd7070Spatrick } 735*e5dd7070Spatrick 736*e5dd7070Spatrick const DeclContext *DC = D->getDeclContext(); 737*e5dd7070Spatrick 738*e5dd7070Spatrick if (auto *CD = dyn_cast<ObjCContainerDecl>(DC)) { 739*e5dd7070Spatrick if (isDebuggingName(CD->getNameAsString())) 740*e5dd7070Spatrick return true; 741*e5dd7070Spatrick } 742*e5dd7070Spatrick 743*e5dd7070Spatrick return false; 744*e5dd7070Spatrick } 745*e5dd7070Spatrick 746*e5dd7070Spatrick 747*e5dd7070Spatrick /// Reports a localization error for the passed in method call and SVal 748*e5dd7070Spatrick void NonLocalizedStringChecker::reportLocalizationError( 749*e5dd7070Spatrick SVal S, const CallEvent &M, CheckerContext &C, int argumentNumber) const { 750*e5dd7070Spatrick 751*e5dd7070Spatrick // Don't warn about localization errors in classes and methods that 752*e5dd7070Spatrick // may be debug code. 753*e5dd7070Spatrick if (isDebuggingContext(C)) 754*e5dd7070Spatrick return; 755*e5dd7070Spatrick 756*e5dd7070Spatrick static CheckerProgramPointTag Tag("NonLocalizedStringChecker", 757*e5dd7070Spatrick "UnlocalizedString"); 758*e5dd7070Spatrick ExplodedNode *ErrNode = C.addTransition(C.getState(), C.getPredecessor(), &Tag); 759*e5dd7070Spatrick 760*e5dd7070Spatrick if (!ErrNode) 761*e5dd7070Spatrick return; 762*e5dd7070Spatrick 763*e5dd7070Spatrick // Generate the bug report. 764*e5dd7070Spatrick auto R = std::make_unique<PathSensitiveBugReport>( 765*e5dd7070Spatrick *BT, "User-facing text should use localized string macro", ErrNode); 766*e5dd7070Spatrick if (argumentNumber) { 767*e5dd7070Spatrick R->addRange(M.getArgExpr(argumentNumber - 1)->getSourceRange()); 768*e5dd7070Spatrick } else { 769*e5dd7070Spatrick R->addRange(M.getSourceRange()); 770*e5dd7070Spatrick } 771*e5dd7070Spatrick R->markInteresting(S); 772*e5dd7070Spatrick 773*e5dd7070Spatrick const MemRegion *StringRegion = S.getAsRegion(); 774*e5dd7070Spatrick if (StringRegion) 775*e5dd7070Spatrick R->addVisitor(std::make_unique<NonLocalizedStringBRVisitor>(StringRegion)); 776*e5dd7070Spatrick 777*e5dd7070Spatrick C.emitReport(std::move(R)); 778*e5dd7070Spatrick } 779*e5dd7070Spatrick 780*e5dd7070Spatrick /// Returns the argument number requiring localized string if it exists 781*e5dd7070Spatrick /// otherwise, returns -1 782*e5dd7070Spatrick int NonLocalizedStringChecker::getLocalizedArgumentForSelector( 783*e5dd7070Spatrick const IdentifierInfo *Receiver, Selector S) const { 784*e5dd7070Spatrick auto method = UIMethods.find(Receiver); 785*e5dd7070Spatrick 786*e5dd7070Spatrick if (method == UIMethods.end()) 787*e5dd7070Spatrick return -1; 788*e5dd7070Spatrick 789*e5dd7070Spatrick auto argumentIterator = method->getSecond().find(S); 790*e5dd7070Spatrick 791*e5dd7070Spatrick if (argumentIterator == method->getSecond().end()) 792*e5dd7070Spatrick return -1; 793*e5dd7070Spatrick 794*e5dd7070Spatrick int argumentNumber = argumentIterator->getSecond(); 795*e5dd7070Spatrick return argumentNumber; 796*e5dd7070Spatrick } 797*e5dd7070Spatrick 798*e5dd7070Spatrick /// Check if the string being passed in has NonLocalized state 799*e5dd7070Spatrick void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 800*e5dd7070Spatrick CheckerContext &C) const { 801*e5dd7070Spatrick initUIMethods(C.getASTContext()); 802*e5dd7070Spatrick 803*e5dd7070Spatrick const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); 804*e5dd7070Spatrick if (!OD) 805*e5dd7070Spatrick return; 806*e5dd7070Spatrick const IdentifierInfo *odInfo = OD->getIdentifier(); 807*e5dd7070Spatrick 808*e5dd7070Spatrick Selector S = msg.getSelector(); 809*e5dd7070Spatrick 810*e5dd7070Spatrick std::string SelectorString = S.getAsString(); 811*e5dd7070Spatrick StringRef SelectorName = SelectorString; 812*e5dd7070Spatrick assert(!SelectorName.empty()); 813*e5dd7070Spatrick 814*e5dd7070Spatrick if (odInfo->isStr("NSString")) { 815*e5dd7070Spatrick // Handle the case where the receiver is an NSString 816*e5dd7070Spatrick // These special NSString methods draw to the screen 817*e5dd7070Spatrick 818*e5dd7070Spatrick if (!(SelectorName.startswith("drawAtPoint") || 819*e5dd7070Spatrick SelectorName.startswith("drawInRect") || 820*e5dd7070Spatrick SelectorName.startswith("drawWithRect"))) 821*e5dd7070Spatrick return; 822*e5dd7070Spatrick 823*e5dd7070Spatrick SVal svTitle = msg.getReceiverSVal(); 824*e5dd7070Spatrick 825*e5dd7070Spatrick bool isNonLocalized = hasNonLocalizedState(svTitle, C); 826*e5dd7070Spatrick 827*e5dd7070Spatrick if (isNonLocalized) { 828*e5dd7070Spatrick reportLocalizationError(svTitle, msg, C); 829*e5dd7070Spatrick } 830*e5dd7070Spatrick } 831*e5dd7070Spatrick 832*e5dd7070Spatrick int argumentNumber = getLocalizedArgumentForSelector(odInfo, S); 833*e5dd7070Spatrick // Go up each hierarchy of superclasses and their protocols 834*e5dd7070Spatrick while (argumentNumber < 0 && OD->getSuperClass() != nullptr) { 835*e5dd7070Spatrick for (const auto *P : OD->all_referenced_protocols()) { 836*e5dd7070Spatrick argumentNumber = getLocalizedArgumentForSelector(P->getIdentifier(), S); 837*e5dd7070Spatrick if (argumentNumber >= 0) 838*e5dd7070Spatrick break; 839*e5dd7070Spatrick } 840*e5dd7070Spatrick if (argumentNumber < 0) { 841*e5dd7070Spatrick OD = OD->getSuperClass(); 842*e5dd7070Spatrick argumentNumber = getLocalizedArgumentForSelector(OD->getIdentifier(), S); 843*e5dd7070Spatrick } 844*e5dd7070Spatrick } 845*e5dd7070Spatrick 846*e5dd7070Spatrick if (argumentNumber < 0) { // There was no match in UIMethods 847*e5dd7070Spatrick if (const Decl *D = msg.getDecl()) { 848*e5dd7070Spatrick if (const ObjCMethodDecl *OMD = dyn_cast_or_null<ObjCMethodDecl>(D)) { 849*e5dd7070Spatrick auto formals = OMD->parameters(); 850*e5dd7070Spatrick for (unsigned i = 0, ei = formals.size(); i != ei; ++i) { 851*e5dd7070Spatrick if (isAnnotatedAsTakingLocalized(formals[i])) { 852*e5dd7070Spatrick argumentNumber = i; 853*e5dd7070Spatrick break; 854*e5dd7070Spatrick } 855*e5dd7070Spatrick } 856*e5dd7070Spatrick } 857*e5dd7070Spatrick } 858*e5dd7070Spatrick } 859*e5dd7070Spatrick 860*e5dd7070Spatrick if (argumentNumber < 0) // Still no match 861*e5dd7070Spatrick return; 862*e5dd7070Spatrick 863*e5dd7070Spatrick SVal svTitle = msg.getArgSVal(argumentNumber); 864*e5dd7070Spatrick 865*e5dd7070Spatrick if (const ObjCStringRegion *SR = 866*e5dd7070Spatrick dyn_cast_or_null<ObjCStringRegion>(svTitle.getAsRegion())) { 867*e5dd7070Spatrick StringRef stringValue = 868*e5dd7070Spatrick SR->getObjCStringLiteral()->getString()->getString(); 869*e5dd7070Spatrick if ((stringValue.trim().size() == 0 && stringValue.size() > 0) || 870*e5dd7070Spatrick stringValue.empty()) 871*e5dd7070Spatrick return; 872*e5dd7070Spatrick if (!IsAggressive && llvm::sys::unicode::columnWidthUTF8(stringValue) < 2) 873*e5dd7070Spatrick return; 874*e5dd7070Spatrick } 875*e5dd7070Spatrick 876*e5dd7070Spatrick bool isNonLocalized = hasNonLocalizedState(svTitle, C); 877*e5dd7070Spatrick 878*e5dd7070Spatrick if (isNonLocalized) { 879*e5dd7070Spatrick reportLocalizationError(svTitle, msg, C, argumentNumber + 1); 880*e5dd7070Spatrick } 881*e5dd7070Spatrick } 882*e5dd7070Spatrick 883*e5dd7070Spatrick void NonLocalizedStringChecker::checkPreCall(const CallEvent &Call, 884*e5dd7070Spatrick CheckerContext &C) const { 885*e5dd7070Spatrick const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 886*e5dd7070Spatrick if (!FD) 887*e5dd7070Spatrick return; 888*e5dd7070Spatrick 889*e5dd7070Spatrick auto formals = FD->parameters(); 890*e5dd7070Spatrick for (unsigned i = 0, ei = std::min(static_cast<unsigned>(formals.size()), 891*e5dd7070Spatrick Call.getNumArgs()); i != ei; ++i) { 892*e5dd7070Spatrick if (isAnnotatedAsTakingLocalized(formals[i])) { 893*e5dd7070Spatrick auto actual = Call.getArgSVal(i); 894*e5dd7070Spatrick if (hasNonLocalizedState(actual, C)) { 895*e5dd7070Spatrick reportLocalizationError(actual, Call, C, i + 1); 896*e5dd7070Spatrick } 897*e5dd7070Spatrick } 898*e5dd7070Spatrick } 899*e5dd7070Spatrick } 900*e5dd7070Spatrick 901*e5dd7070Spatrick static inline bool isNSStringType(QualType T, ASTContext &Ctx) { 902*e5dd7070Spatrick 903*e5dd7070Spatrick const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); 904*e5dd7070Spatrick if (!PT) 905*e5dd7070Spatrick return false; 906*e5dd7070Spatrick 907*e5dd7070Spatrick ObjCInterfaceDecl *Cls = PT->getObjectType()->getInterface(); 908*e5dd7070Spatrick if (!Cls) 909*e5dd7070Spatrick return false; 910*e5dd7070Spatrick 911*e5dd7070Spatrick IdentifierInfo *ClsName = Cls->getIdentifier(); 912*e5dd7070Spatrick 913*e5dd7070Spatrick // FIXME: Should we walk the chain of classes? 914*e5dd7070Spatrick return ClsName == &Ctx.Idents.get("NSString") || 915*e5dd7070Spatrick ClsName == &Ctx.Idents.get("NSMutableString"); 916*e5dd7070Spatrick } 917*e5dd7070Spatrick 918*e5dd7070Spatrick /// Marks a string being returned by any call as localized 919*e5dd7070Spatrick /// if it is in LocStringFunctions (LSF) or the function is annotated. 920*e5dd7070Spatrick /// Otherwise, we mark it as NonLocalized (Aggressive) or 921*e5dd7070Spatrick /// NonLocalized only if it is not backed by a SymRegion (Non-Aggressive), 922*e5dd7070Spatrick /// basically leaving only string literals as NonLocalized. 923*e5dd7070Spatrick void NonLocalizedStringChecker::checkPostCall(const CallEvent &Call, 924*e5dd7070Spatrick CheckerContext &C) const { 925*e5dd7070Spatrick initLocStringsMethods(C.getASTContext()); 926*e5dd7070Spatrick 927*e5dd7070Spatrick if (!Call.getOriginExpr()) 928*e5dd7070Spatrick return; 929*e5dd7070Spatrick 930*e5dd7070Spatrick // Anything that takes in a localized NSString as an argument 931*e5dd7070Spatrick // and returns an NSString will be assumed to be returning a 932*e5dd7070Spatrick // localized NSString. (Counter: Incorrectly combining two LocalizedStrings) 933*e5dd7070Spatrick const QualType RT = Call.getResultType(); 934*e5dd7070Spatrick if (isNSStringType(RT, C.getASTContext())) { 935*e5dd7070Spatrick for (unsigned i = 0; i < Call.getNumArgs(); ++i) { 936*e5dd7070Spatrick SVal argValue = Call.getArgSVal(i); 937*e5dd7070Spatrick if (hasLocalizedState(argValue, C)) { 938*e5dd7070Spatrick SVal sv = Call.getReturnValue(); 939*e5dd7070Spatrick setLocalizedState(sv, C); 940*e5dd7070Spatrick return; 941*e5dd7070Spatrick } 942*e5dd7070Spatrick } 943*e5dd7070Spatrick } 944*e5dd7070Spatrick 945*e5dd7070Spatrick const Decl *D = Call.getDecl(); 946*e5dd7070Spatrick if (!D) 947*e5dd7070Spatrick return; 948*e5dd7070Spatrick 949*e5dd7070Spatrick const IdentifierInfo *Identifier = Call.getCalleeIdentifier(); 950*e5dd7070Spatrick 951*e5dd7070Spatrick SVal sv = Call.getReturnValue(); 952*e5dd7070Spatrick if (isAnnotatedAsReturningLocalized(D) || LSF.count(Identifier) != 0) { 953*e5dd7070Spatrick setLocalizedState(sv, C); 954*e5dd7070Spatrick } else if (isNSStringType(RT, C.getASTContext()) && 955*e5dd7070Spatrick !hasLocalizedState(sv, C)) { 956*e5dd7070Spatrick if (IsAggressive) { 957*e5dd7070Spatrick setNonLocalizedState(sv, C); 958*e5dd7070Spatrick } else { 959*e5dd7070Spatrick const SymbolicRegion *SymReg = 960*e5dd7070Spatrick dyn_cast_or_null<SymbolicRegion>(sv.getAsRegion()); 961*e5dd7070Spatrick if (!SymReg) 962*e5dd7070Spatrick setNonLocalizedState(sv, C); 963*e5dd7070Spatrick } 964*e5dd7070Spatrick } 965*e5dd7070Spatrick } 966*e5dd7070Spatrick 967*e5dd7070Spatrick /// Marks a string being returned by an ObjC method as localized 968*e5dd7070Spatrick /// if it is in LocStringMethods or the method is annotated 969*e5dd7070Spatrick void NonLocalizedStringChecker::checkPostObjCMessage(const ObjCMethodCall &msg, 970*e5dd7070Spatrick CheckerContext &C) const { 971*e5dd7070Spatrick initLocStringsMethods(C.getASTContext()); 972*e5dd7070Spatrick 973*e5dd7070Spatrick if (!msg.isInstanceMessage()) 974*e5dd7070Spatrick return; 975*e5dd7070Spatrick 976*e5dd7070Spatrick const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); 977*e5dd7070Spatrick if (!OD) 978*e5dd7070Spatrick return; 979*e5dd7070Spatrick const IdentifierInfo *odInfo = OD->getIdentifier(); 980*e5dd7070Spatrick 981*e5dd7070Spatrick Selector S = msg.getSelector(); 982*e5dd7070Spatrick std::string SelectorName = S.getAsString(); 983*e5dd7070Spatrick 984*e5dd7070Spatrick std::pair<const IdentifierInfo *, Selector> MethodDescription = {odInfo, S}; 985*e5dd7070Spatrick 986*e5dd7070Spatrick if (LSM.count(MethodDescription) || 987*e5dd7070Spatrick isAnnotatedAsReturningLocalized(msg.getDecl())) { 988*e5dd7070Spatrick SVal sv = msg.getReturnValue(); 989*e5dd7070Spatrick setLocalizedState(sv, C); 990*e5dd7070Spatrick } 991*e5dd7070Spatrick } 992*e5dd7070Spatrick 993*e5dd7070Spatrick /// Marks all empty string literals as localized 994*e5dd7070Spatrick void NonLocalizedStringChecker::checkPostStmt(const ObjCStringLiteral *SL, 995*e5dd7070Spatrick CheckerContext &C) const { 996*e5dd7070Spatrick SVal sv = C.getSVal(SL); 997*e5dd7070Spatrick setNonLocalizedState(sv, C); 998*e5dd7070Spatrick } 999*e5dd7070Spatrick 1000*e5dd7070Spatrick PathDiagnosticPieceRef 1001*e5dd7070Spatrick NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ, 1002*e5dd7070Spatrick BugReporterContext &BRC, 1003*e5dd7070Spatrick PathSensitiveBugReport &BR) { 1004*e5dd7070Spatrick if (Satisfied) 1005*e5dd7070Spatrick return nullptr; 1006*e5dd7070Spatrick 1007*e5dd7070Spatrick Optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>(); 1008*e5dd7070Spatrick if (!Point.hasValue()) 1009*e5dd7070Spatrick return nullptr; 1010*e5dd7070Spatrick 1011*e5dd7070Spatrick auto *LiteralExpr = dyn_cast<ObjCStringLiteral>(Point->getStmt()); 1012*e5dd7070Spatrick if (!LiteralExpr) 1013*e5dd7070Spatrick return nullptr; 1014*e5dd7070Spatrick 1015*e5dd7070Spatrick SVal LiteralSVal = Succ->getSVal(LiteralExpr); 1016*e5dd7070Spatrick if (LiteralSVal.getAsRegion() != NonLocalizedString) 1017*e5dd7070Spatrick return nullptr; 1018*e5dd7070Spatrick 1019*e5dd7070Spatrick Satisfied = true; 1020*e5dd7070Spatrick 1021*e5dd7070Spatrick PathDiagnosticLocation L = 1022*e5dd7070Spatrick PathDiagnosticLocation::create(*Point, BRC.getSourceManager()); 1023*e5dd7070Spatrick 1024*e5dd7070Spatrick if (!L.isValid() || !L.asLocation().isValid()) 1025*e5dd7070Spatrick return nullptr; 1026*e5dd7070Spatrick 1027*e5dd7070Spatrick auto Piece = std::make_shared<PathDiagnosticEventPiece>( 1028*e5dd7070Spatrick L, "Non-localized string literal here"); 1029*e5dd7070Spatrick Piece->addRange(LiteralExpr->getSourceRange()); 1030*e5dd7070Spatrick 1031*e5dd7070Spatrick return std::move(Piece); 1032*e5dd7070Spatrick } 1033*e5dd7070Spatrick 1034*e5dd7070Spatrick namespace { 1035*e5dd7070Spatrick class EmptyLocalizationContextChecker 1036*e5dd7070Spatrick : public Checker<check::ASTDecl<ObjCImplementationDecl>> { 1037*e5dd7070Spatrick 1038*e5dd7070Spatrick // A helper class, which walks the AST 1039*e5dd7070Spatrick class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { 1040*e5dd7070Spatrick const ObjCMethodDecl *MD; 1041*e5dd7070Spatrick BugReporter &BR; 1042*e5dd7070Spatrick AnalysisManager &Mgr; 1043*e5dd7070Spatrick const CheckerBase *Checker; 1044*e5dd7070Spatrick LocationOrAnalysisDeclContext DCtx; 1045*e5dd7070Spatrick 1046*e5dd7070Spatrick public: 1047*e5dd7070Spatrick MethodCrawler(const ObjCMethodDecl *InMD, BugReporter &InBR, 1048*e5dd7070Spatrick const CheckerBase *Checker, AnalysisManager &InMgr, 1049*e5dd7070Spatrick AnalysisDeclContext *InDCtx) 1050*e5dd7070Spatrick : MD(InMD), BR(InBR), Mgr(InMgr), Checker(Checker), DCtx(InDCtx) {} 1051*e5dd7070Spatrick 1052*e5dd7070Spatrick void VisitStmt(const Stmt *S) { VisitChildren(S); } 1053*e5dd7070Spatrick 1054*e5dd7070Spatrick void VisitObjCMessageExpr(const ObjCMessageExpr *ME); 1055*e5dd7070Spatrick 1056*e5dd7070Spatrick void reportEmptyContextError(const ObjCMessageExpr *M) const; 1057*e5dd7070Spatrick 1058*e5dd7070Spatrick void VisitChildren(const Stmt *S) { 1059*e5dd7070Spatrick for (const Stmt *Child : S->children()) { 1060*e5dd7070Spatrick if (Child) 1061*e5dd7070Spatrick this->Visit(Child); 1062*e5dd7070Spatrick } 1063*e5dd7070Spatrick } 1064*e5dd7070Spatrick }; 1065*e5dd7070Spatrick 1066*e5dd7070Spatrick public: 1067*e5dd7070Spatrick void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, 1068*e5dd7070Spatrick BugReporter &BR) const; 1069*e5dd7070Spatrick }; 1070*e5dd7070Spatrick } // end anonymous namespace 1071*e5dd7070Spatrick 1072*e5dd7070Spatrick void EmptyLocalizationContextChecker::checkASTDecl( 1073*e5dd7070Spatrick const ObjCImplementationDecl *D, AnalysisManager &Mgr, 1074*e5dd7070Spatrick BugReporter &BR) const { 1075*e5dd7070Spatrick 1076*e5dd7070Spatrick for (const ObjCMethodDecl *M : D->methods()) { 1077*e5dd7070Spatrick AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M); 1078*e5dd7070Spatrick 1079*e5dd7070Spatrick const Stmt *Body = M->getBody(); 1080*e5dd7070Spatrick if (!Body) { 1081*e5dd7070Spatrick assert(M->isSynthesizedAccessorStub()); 1082*e5dd7070Spatrick continue; 1083*e5dd7070Spatrick } 1084*e5dd7070Spatrick 1085*e5dd7070Spatrick MethodCrawler MC(M->getCanonicalDecl(), BR, this, Mgr, DCtx); 1086*e5dd7070Spatrick MC.VisitStmt(Body); 1087*e5dd7070Spatrick } 1088*e5dd7070Spatrick } 1089*e5dd7070Spatrick 1090*e5dd7070Spatrick /// This check attempts to match these macros, assuming they are defined as 1091*e5dd7070Spatrick /// follows: 1092*e5dd7070Spatrick /// 1093*e5dd7070Spatrick /// #define NSLocalizedString(key, comment) \ 1094*e5dd7070Spatrick /// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil] 1095*e5dd7070Spatrick /// #define NSLocalizedStringFromTable(key, tbl, comment) \ 1096*e5dd7070Spatrick /// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)] 1097*e5dd7070Spatrick /// #define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \ 1098*e5dd7070Spatrick /// [bundle localizedStringForKey:(key) value:@"" table:(tbl)] 1099*e5dd7070Spatrick /// #define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) 1100*e5dd7070Spatrick /// 1101*e5dd7070Spatrick /// We cannot use the path sensitive check because the macro argument we are 1102*e5dd7070Spatrick /// checking for (comment) is not used and thus not present in the AST, 1103*e5dd7070Spatrick /// so we use Lexer on the original macro call and retrieve the value of 1104*e5dd7070Spatrick /// the comment. If it's empty or nil, we raise a warning. 1105*e5dd7070Spatrick void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr( 1106*e5dd7070Spatrick const ObjCMessageExpr *ME) { 1107*e5dd7070Spatrick 1108*e5dd7070Spatrick // FIXME: We may be able to use PPCallbacks to check for empty context 1109*e5dd7070Spatrick // comments as part of preprocessing and avoid this re-lexing hack. 1110*e5dd7070Spatrick const ObjCInterfaceDecl *OD = ME->getReceiverInterface(); 1111*e5dd7070Spatrick if (!OD) 1112*e5dd7070Spatrick return; 1113*e5dd7070Spatrick 1114*e5dd7070Spatrick const IdentifierInfo *odInfo = OD->getIdentifier(); 1115*e5dd7070Spatrick 1116*e5dd7070Spatrick if (!(odInfo->isStr("NSBundle") && 1117*e5dd7070Spatrick ME->getSelector().getAsString() == 1118*e5dd7070Spatrick "localizedStringForKey:value:table:")) { 1119*e5dd7070Spatrick return; 1120*e5dd7070Spatrick } 1121*e5dd7070Spatrick 1122*e5dd7070Spatrick SourceRange R = ME->getSourceRange(); 1123*e5dd7070Spatrick if (!R.getBegin().isMacroID()) 1124*e5dd7070Spatrick return; 1125*e5dd7070Spatrick 1126*e5dd7070Spatrick // getImmediateMacroCallerLoc gets the location of the immediate macro 1127*e5dd7070Spatrick // caller, one level up the stack toward the initial macro typed into the 1128*e5dd7070Spatrick // source, so SL should point to the NSLocalizedString macro. 1129*e5dd7070Spatrick SourceLocation SL = 1130*e5dd7070Spatrick Mgr.getSourceManager().getImmediateMacroCallerLoc(R.getBegin()); 1131*e5dd7070Spatrick std::pair<FileID, unsigned> SLInfo = 1132*e5dd7070Spatrick Mgr.getSourceManager().getDecomposedLoc(SL); 1133*e5dd7070Spatrick 1134*e5dd7070Spatrick SrcMgr::SLocEntry SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first); 1135*e5dd7070Spatrick 1136*e5dd7070Spatrick // If NSLocalizedString macro is wrapped in another macro, we need to 1137*e5dd7070Spatrick // unwrap the expansion until we get to the NSLocalizedStringMacro. 1138*e5dd7070Spatrick while (SE.isExpansion()) { 1139*e5dd7070Spatrick SL = SE.getExpansion().getSpellingLoc(); 1140*e5dd7070Spatrick SLInfo = Mgr.getSourceManager().getDecomposedLoc(SL); 1141*e5dd7070Spatrick SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first); 1142*e5dd7070Spatrick } 1143*e5dd7070Spatrick 1144*e5dd7070Spatrick bool Invalid = false; 1145*e5dd7070Spatrick const llvm::MemoryBuffer *BF = 1146*e5dd7070Spatrick Mgr.getSourceManager().getBuffer(SLInfo.first, SL, &Invalid); 1147*e5dd7070Spatrick if (Invalid) 1148*e5dd7070Spatrick return; 1149*e5dd7070Spatrick 1150*e5dd7070Spatrick Lexer TheLexer(SL, LangOptions(), BF->getBufferStart(), 1151*e5dd7070Spatrick BF->getBufferStart() + SLInfo.second, BF->getBufferEnd()); 1152*e5dd7070Spatrick 1153*e5dd7070Spatrick Token I; 1154*e5dd7070Spatrick Token Result; // This will hold the token just before the last ')' 1155*e5dd7070Spatrick int p_count = 0; // This is for parenthesis matching 1156*e5dd7070Spatrick while (!TheLexer.LexFromRawLexer(I)) { 1157*e5dd7070Spatrick if (I.getKind() == tok::l_paren) 1158*e5dd7070Spatrick ++p_count; 1159*e5dd7070Spatrick if (I.getKind() == tok::r_paren) { 1160*e5dd7070Spatrick if (p_count == 1) 1161*e5dd7070Spatrick break; 1162*e5dd7070Spatrick --p_count; 1163*e5dd7070Spatrick } 1164*e5dd7070Spatrick Result = I; 1165*e5dd7070Spatrick } 1166*e5dd7070Spatrick 1167*e5dd7070Spatrick if (isAnyIdentifier(Result.getKind())) { 1168*e5dd7070Spatrick if (Result.getRawIdentifier().equals("nil")) { 1169*e5dd7070Spatrick reportEmptyContextError(ME); 1170*e5dd7070Spatrick return; 1171*e5dd7070Spatrick } 1172*e5dd7070Spatrick } 1173*e5dd7070Spatrick 1174*e5dd7070Spatrick if (!isStringLiteral(Result.getKind())) 1175*e5dd7070Spatrick return; 1176*e5dd7070Spatrick 1177*e5dd7070Spatrick StringRef Comment = 1178*e5dd7070Spatrick StringRef(Result.getLiteralData(), Result.getLength()).trim('"'); 1179*e5dd7070Spatrick 1180*e5dd7070Spatrick if ((Comment.trim().size() == 0 && Comment.size() > 0) || // Is Whitespace 1181*e5dd7070Spatrick Comment.empty()) { 1182*e5dd7070Spatrick reportEmptyContextError(ME); 1183*e5dd7070Spatrick } 1184*e5dd7070Spatrick } 1185*e5dd7070Spatrick 1186*e5dd7070Spatrick void EmptyLocalizationContextChecker::MethodCrawler::reportEmptyContextError( 1187*e5dd7070Spatrick const ObjCMessageExpr *ME) const { 1188*e5dd7070Spatrick // Generate the bug report. 1189*e5dd7070Spatrick BR.EmitBasicReport(MD, Checker, "Context Missing", 1190*e5dd7070Spatrick "Localizability Issue (Apple)", 1191*e5dd7070Spatrick "Localized string macro should include a non-empty " 1192*e5dd7070Spatrick "comment for translators", 1193*e5dd7070Spatrick PathDiagnosticLocation(ME, BR.getSourceManager(), DCtx)); 1194*e5dd7070Spatrick } 1195*e5dd7070Spatrick 1196*e5dd7070Spatrick namespace { 1197*e5dd7070Spatrick class PluralMisuseChecker : public Checker<check::ASTCodeBody> { 1198*e5dd7070Spatrick 1199*e5dd7070Spatrick // A helper class, which walks the AST 1200*e5dd7070Spatrick class MethodCrawler : public RecursiveASTVisitor<MethodCrawler> { 1201*e5dd7070Spatrick BugReporter &BR; 1202*e5dd7070Spatrick const CheckerBase *Checker; 1203*e5dd7070Spatrick AnalysisDeclContext *AC; 1204*e5dd7070Spatrick 1205*e5dd7070Spatrick // This functions like a stack. We push on any IfStmt or 1206*e5dd7070Spatrick // ConditionalOperator that matches the condition 1207*e5dd7070Spatrick // and pop it off when we leave that statement 1208*e5dd7070Spatrick llvm::SmallVector<const clang::Stmt *, 8> MatchingStatements; 1209*e5dd7070Spatrick // This is true when we are the direct-child of a 1210*e5dd7070Spatrick // matching statement 1211*e5dd7070Spatrick bool InMatchingStatement = false; 1212*e5dd7070Spatrick 1213*e5dd7070Spatrick public: 1214*e5dd7070Spatrick explicit MethodCrawler(BugReporter &InBR, const CheckerBase *Checker, 1215*e5dd7070Spatrick AnalysisDeclContext *InAC) 1216*e5dd7070Spatrick : BR(InBR), Checker(Checker), AC(InAC) {} 1217*e5dd7070Spatrick 1218*e5dd7070Spatrick bool VisitIfStmt(const IfStmt *I); 1219*e5dd7070Spatrick bool EndVisitIfStmt(IfStmt *I); 1220*e5dd7070Spatrick bool TraverseIfStmt(IfStmt *x); 1221*e5dd7070Spatrick bool VisitConditionalOperator(const ConditionalOperator *C); 1222*e5dd7070Spatrick bool TraverseConditionalOperator(ConditionalOperator *C); 1223*e5dd7070Spatrick bool VisitCallExpr(const CallExpr *CE); 1224*e5dd7070Spatrick bool VisitObjCMessageExpr(const ObjCMessageExpr *ME); 1225*e5dd7070Spatrick 1226*e5dd7070Spatrick private: 1227*e5dd7070Spatrick void reportPluralMisuseError(const Stmt *S) const; 1228*e5dd7070Spatrick bool isCheckingPlurality(const Expr *E) const; 1229*e5dd7070Spatrick }; 1230*e5dd7070Spatrick 1231*e5dd7070Spatrick public: 1232*e5dd7070Spatrick void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, 1233*e5dd7070Spatrick BugReporter &BR) const { 1234*e5dd7070Spatrick MethodCrawler Visitor(BR, this, Mgr.getAnalysisDeclContext(D)); 1235*e5dd7070Spatrick Visitor.TraverseDecl(const_cast<Decl *>(D)); 1236*e5dd7070Spatrick } 1237*e5dd7070Spatrick }; 1238*e5dd7070Spatrick } // end anonymous namespace 1239*e5dd7070Spatrick 1240*e5dd7070Spatrick // Checks the condition of the IfStmt and returns true if one 1241*e5dd7070Spatrick // of the following heuristics are met: 1242*e5dd7070Spatrick // 1) The conidtion is a variable with "singular" or "plural" in the name 1243*e5dd7070Spatrick // 2) The condition is a binary operator with 1 or 2 on the right-hand side 1244*e5dd7070Spatrick bool PluralMisuseChecker::MethodCrawler::isCheckingPlurality( 1245*e5dd7070Spatrick const Expr *Condition) const { 1246*e5dd7070Spatrick const BinaryOperator *BO = nullptr; 1247*e5dd7070Spatrick // Accounts for when a VarDecl represents a BinaryOperator 1248*e5dd7070Spatrick if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Condition)) { 1249*e5dd7070Spatrick if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 1250*e5dd7070Spatrick const Expr *InitExpr = VD->getInit(); 1251*e5dd7070Spatrick if (InitExpr) { 1252*e5dd7070Spatrick if (const BinaryOperator *B = 1253*e5dd7070Spatrick dyn_cast<BinaryOperator>(InitExpr->IgnoreParenImpCasts())) { 1254*e5dd7070Spatrick BO = B; 1255*e5dd7070Spatrick } 1256*e5dd7070Spatrick } 1257*e5dd7070Spatrick if (VD->getName().lower().find("plural") != StringRef::npos || 1258*e5dd7070Spatrick VD->getName().lower().find("singular") != StringRef::npos) { 1259*e5dd7070Spatrick return true; 1260*e5dd7070Spatrick } 1261*e5dd7070Spatrick } 1262*e5dd7070Spatrick } else if (const BinaryOperator *B = dyn_cast<BinaryOperator>(Condition)) { 1263*e5dd7070Spatrick BO = B; 1264*e5dd7070Spatrick } 1265*e5dd7070Spatrick 1266*e5dd7070Spatrick if (BO == nullptr) 1267*e5dd7070Spatrick return false; 1268*e5dd7070Spatrick 1269*e5dd7070Spatrick if (IntegerLiteral *IL = dyn_cast_or_null<IntegerLiteral>( 1270*e5dd7070Spatrick BO->getRHS()->IgnoreParenImpCasts())) { 1271*e5dd7070Spatrick llvm::APInt Value = IL->getValue(); 1272*e5dd7070Spatrick if (Value == 1 || Value == 2) { 1273*e5dd7070Spatrick return true; 1274*e5dd7070Spatrick } 1275*e5dd7070Spatrick } 1276*e5dd7070Spatrick return false; 1277*e5dd7070Spatrick } 1278*e5dd7070Spatrick 1279*e5dd7070Spatrick // A CallExpr with "LOC" in its identifier that takes in a string literal 1280*e5dd7070Spatrick // has been shown to almost always be a function that returns a localized 1281*e5dd7070Spatrick // string. Raise a diagnostic when this is in a statement that matches 1282*e5dd7070Spatrick // the condition. 1283*e5dd7070Spatrick bool PluralMisuseChecker::MethodCrawler::VisitCallExpr(const CallExpr *CE) { 1284*e5dd7070Spatrick if (InMatchingStatement) { 1285*e5dd7070Spatrick if (const FunctionDecl *FD = CE->getDirectCallee()) { 1286*e5dd7070Spatrick std::string NormalizedName = 1287*e5dd7070Spatrick StringRef(FD->getNameInfo().getAsString()).lower(); 1288*e5dd7070Spatrick if (NormalizedName.find("loc") != std::string::npos) { 1289*e5dd7070Spatrick for (const Expr *Arg : CE->arguments()) { 1290*e5dd7070Spatrick if (isa<ObjCStringLiteral>(Arg)) 1291*e5dd7070Spatrick reportPluralMisuseError(CE); 1292*e5dd7070Spatrick } 1293*e5dd7070Spatrick } 1294*e5dd7070Spatrick } 1295*e5dd7070Spatrick } 1296*e5dd7070Spatrick return true; 1297*e5dd7070Spatrick } 1298*e5dd7070Spatrick 1299*e5dd7070Spatrick // The other case is for NSLocalizedString which also returns 1300*e5dd7070Spatrick // a localized string. It's a macro for the ObjCMessageExpr 1301*e5dd7070Spatrick // [NSBundle localizedStringForKey:value:table:] Raise a 1302*e5dd7070Spatrick // diagnostic when this is in a statement that matches 1303*e5dd7070Spatrick // the condition. 1304*e5dd7070Spatrick bool PluralMisuseChecker::MethodCrawler::VisitObjCMessageExpr( 1305*e5dd7070Spatrick const ObjCMessageExpr *ME) { 1306*e5dd7070Spatrick const ObjCInterfaceDecl *OD = ME->getReceiverInterface(); 1307*e5dd7070Spatrick if (!OD) 1308*e5dd7070Spatrick return true; 1309*e5dd7070Spatrick 1310*e5dd7070Spatrick const IdentifierInfo *odInfo = OD->getIdentifier(); 1311*e5dd7070Spatrick 1312*e5dd7070Spatrick if (odInfo->isStr("NSBundle") && 1313*e5dd7070Spatrick ME->getSelector().getAsString() == "localizedStringForKey:value:table:") { 1314*e5dd7070Spatrick if (InMatchingStatement) { 1315*e5dd7070Spatrick reportPluralMisuseError(ME); 1316*e5dd7070Spatrick } 1317*e5dd7070Spatrick } 1318*e5dd7070Spatrick return true; 1319*e5dd7070Spatrick } 1320*e5dd7070Spatrick 1321*e5dd7070Spatrick /// Override TraverseIfStmt so we know when we are done traversing an IfStmt 1322*e5dd7070Spatrick bool PluralMisuseChecker::MethodCrawler::TraverseIfStmt(IfStmt *I) { 1323*e5dd7070Spatrick RecursiveASTVisitor<MethodCrawler>::TraverseIfStmt(I); 1324*e5dd7070Spatrick return EndVisitIfStmt(I); 1325*e5dd7070Spatrick } 1326*e5dd7070Spatrick 1327*e5dd7070Spatrick // EndVisit callbacks are not provided by the RecursiveASTVisitor 1328*e5dd7070Spatrick // so we override TraverseIfStmt and make a call to EndVisitIfStmt 1329*e5dd7070Spatrick // after traversing the IfStmt 1330*e5dd7070Spatrick bool PluralMisuseChecker::MethodCrawler::EndVisitIfStmt(IfStmt *I) { 1331*e5dd7070Spatrick MatchingStatements.pop_back(); 1332*e5dd7070Spatrick if (!MatchingStatements.empty()) { 1333*e5dd7070Spatrick if (MatchingStatements.back() != nullptr) { 1334*e5dd7070Spatrick InMatchingStatement = true; 1335*e5dd7070Spatrick return true; 1336*e5dd7070Spatrick } 1337*e5dd7070Spatrick } 1338*e5dd7070Spatrick InMatchingStatement = false; 1339*e5dd7070Spatrick return true; 1340*e5dd7070Spatrick } 1341*e5dd7070Spatrick 1342*e5dd7070Spatrick bool PluralMisuseChecker::MethodCrawler::VisitIfStmt(const IfStmt *I) { 1343*e5dd7070Spatrick const Expr *Condition = I->getCond()->IgnoreParenImpCasts(); 1344*e5dd7070Spatrick if (isCheckingPlurality(Condition)) { 1345*e5dd7070Spatrick MatchingStatements.push_back(I); 1346*e5dd7070Spatrick InMatchingStatement = true; 1347*e5dd7070Spatrick } else { 1348*e5dd7070Spatrick MatchingStatements.push_back(nullptr); 1349*e5dd7070Spatrick InMatchingStatement = false; 1350*e5dd7070Spatrick } 1351*e5dd7070Spatrick 1352*e5dd7070Spatrick return true; 1353*e5dd7070Spatrick } 1354*e5dd7070Spatrick 1355*e5dd7070Spatrick // Preliminary support for conditional operators. 1356*e5dd7070Spatrick bool PluralMisuseChecker::MethodCrawler::TraverseConditionalOperator( 1357*e5dd7070Spatrick ConditionalOperator *C) { 1358*e5dd7070Spatrick RecursiveASTVisitor<MethodCrawler>::TraverseConditionalOperator(C); 1359*e5dd7070Spatrick MatchingStatements.pop_back(); 1360*e5dd7070Spatrick if (!MatchingStatements.empty()) { 1361*e5dd7070Spatrick if (MatchingStatements.back() != nullptr) 1362*e5dd7070Spatrick InMatchingStatement = true; 1363*e5dd7070Spatrick else 1364*e5dd7070Spatrick InMatchingStatement = false; 1365*e5dd7070Spatrick } else { 1366*e5dd7070Spatrick InMatchingStatement = false; 1367*e5dd7070Spatrick } 1368*e5dd7070Spatrick return true; 1369*e5dd7070Spatrick } 1370*e5dd7070Spatrick 1371*e5dd7070Spatrick bool PluralMisuseChecker::MethodCrawler::VisitConditionalOperator( 1372*e5dd7070Spatrick const ConditionalOperator *C) { 1373*e5dd7070Spatrick const Expr *Condition = C->getCond()->IgnoreParenImpCasts(); 1374*e5dd7070Spatrick if (isCheckingPlurality(Condition)) { 1375*e5dd7070Spatrick MatchingStatements.push_back(C); 1376*e5dd7070Spatrick InMatchingStatement = true; 1377*e5dd7070Spatrick } else { 1378*e5dd7070Spatrick MatchingStatements.push_back(nullptr); 1379*e5dd7070Spatrick InMatchingStatement = false; 1380*e5dd7070Spatrick } 1381*e5dd7070Spatrick return true; 1382*e5dd7070Spatrick } 1383*e5dd7070Spatrick 1384*e5dd7070Spatrick void PluralMisuseChecker::MethodCrawler::reportPluralMisuseError( 1385*e5dd7070Spatrick const Stmt *S) const { 1386*e5dd7070Spatrick // Generate the bug report. 1387*e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), Checker, "Plural Misuse", 1388*e5dd7070Spatrick "Localizability Issue (Apple)", 1389*e5dd7070Spatrick "Plural cases are not supported across all languages. " 1390*e5dd7070Spatrick "Use a .stringsdict file instead", 1391*e5dd7070Spatrick PathDiagnosticLocation(S, BR.getSourceManager(), AC)); 1392*e5dd7070Spatrick } 1393*e5dd7070Spatrick 1394*e5dd7070Spatrick //===----------------------------------------------------------------------===// 1395*e5dd7070Spatrick // Checker registration. 1396*e5dd7070Spatrick //===----------------------------------------------------------------------===// 1397*e5dd7070Spatrick 1398*e5dd7070Spatrick void ento::registerNonLocalizedStringChecker(CheckerManager &mgr) { 1399*e5dd7070Spatrick NonLocalizedStringChecker *checker = 1400*e5dd7070Spatrick mgr.registerChecker<NonLocalizedStringChecker>(); 1401*e5dd7070Spatrick checker->IsAggressive = 1402*e5dd7070Spatrick mgr.getAnalyzerOptions().getCheckerBooleanOption( 1403*e5dd7070Spatrick checker, "AggressiveReport"); 1404*e5dd7070Spatrick } 1405*e5dd7070Spatrick 1406*e5dd7070Spatrick bool ento::shouldRegisterNonLocalizedStringChecker(const LangOptions &LO) { 1407*e5dd7070Spatrick return true; 1408*e5dd7070Spatrick } 1409*e5dd7070Spatrick 1410*e5dd7070Spatrick void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) { 1411*e5dd7070Spatrick mgr.registerChecker<EmptyLocalizationContextChecker>(); 1412*e5dd7070Spatrick } 1413*e5dd7070Spatrick 1414*e5dd7070Spatrick bool ento::shouldRegisterEmptyLocalizationContextChecker( 1415*e5dd7070Spatrick const LangOptions &LO) { 1416*e5dd7070Spatrick return true; 1417*e5dd7070Spatrick } 1418*e5dd7070Spatrick 1419*e5dd7070Spatrick void ento::registerPluralMisuseChecker(CheckerManager &mgr) { 1420*e5dd7070Spatrick mgr.registerChecker<PluralMisuseChecker>(); 1421*e5dd7070Spatrick } 1422*e5dd7070Spatrick 1423*e5dd7070Spatrick bool ento::shouldRegisterPluralMisuseChecker(const LangOptions &LO) { 1424*e5dd7070Spatrick return true; 1425*e5dd7070Spatrick } 1426