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