10b57cec5SDimitry Andric //=- LocalizationChecker.cpp -------------------------------------*- C++ -*-==// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file defines a set of checks for localizability including: 100b57cec5SDimitry Andric // 1) A checker that warns about uses of non-localized NSStrings passed to 110b57cec5SDimitry Andric // UI methods expecting localized strings 120b57cec5SDimitry Andric // 2) A syntactic checker that warns against the bad practice of 130b57cec5SDimitry Andric // not including a comment in NSLocalizedString macros. 140b57cec5SDimitry Andric // 150b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 160b57cec5SDimitry Andric 170b57cec5SDimitry Andric #include "clang/AST/Attr.h" 180b57cec5SDimitry Andric #include "clang/AST/Decl.h" 190b57cec5SDimitry Andric #include "clang/AST/DeclObjC.h" 200b57cec5SDimitry Andric #include "clang/AST/RecursiveASTVisitor.h" 210b57cec5SDimitry Andric #include "clang/AST/StmtVisitor.h" 220b57cec5SDimitry Andric #include "clang/Lex/Lexer.h" 2306c3fb27SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 250b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 260b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 270b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 280b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 290b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 300b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 3106c3fb27SDimitry Andric #include "llvm/ADT/STLExtras.h" 320b57cec5SDimitry Andric #include "llvm/Support/Unicode.h" 33bdd1243dSDimitry Andric #include <optional> 340b57cec5SDimitry Andric 350b57cec5SDimitry Andric using namespace clang; 360b57cec5SDimitry Andric using namespace ento; 370b57cec5SDimitry Andric 380b57cec5SDimitry Andric namespace { 390b57cec5SDimitry Andric struct LocalizedState { 400b57cec5SDimitry Andric private: 410b57cec5SDimitry Andric enum Kind { NonLocalized, Localized } K; 420b57cec5SDimitry Andric LocalizedState(Kind InK) : K(InK) {} 430b57cec5SDimitry Andric 440b57cec5SDimitry Andric public: 450b57cec5SDimitry Andric bool isLocalized() const { return K == Localized; } 460b57cec5SDimitry Andric bool isNonLocalized() const { return K == NonLocalized; } 470b57cec5SDimitry Andric 480b57cec5SDimitry Andric static LocalizedState getLocalized() { return LocalizedState(Localized); } 490b57cec5SDimitry Andric static LocalizedState getNonLocalized() { 500b57cec5SDimitry Andric return LocalizedState(NonLocalized); 510b57cec5SDimitry Andric } 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric // Overload the == operator 540b57cec5SDimitry Andric bool operator==(const LocalizedState &X) const { return K == X.K; } 550b57cec5SDimitry Andric 560b57cec5SDimitry Andric // LLVMs equivalent of a hash function 570b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } 580b57cec5SDimitry Andric }; 590b57cec5SDimitry Andric 600b57cec5SDimitry Andric class NonLocalizedStringChecker 610b57cec5SDimitry Andric : public Checker<check::PreCall, check::PostCall, check::PreObjCMessage, 620b57cec5SDimitry Andric check::PostObjCMessage, 630b57cec5SDimitry Andric check::PostStmt<ObjCStringLiteral>> { 640b57cec5SDimitry Andric 65647cbc5dSDimitry Andric const BugType BT{this, "Unlocalizable string", 66647cbc5dSDimitry Andric "Localizability Issue (Apple)"}; 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric // Methods that require a localized string 690b57cec5SDimitry Andric mutable llvm::DenseMap<const IdentifierInfo *, 700b57cec5SDimitry Andric llvm::DenseMap<Selector, uint8_t>> UIMethods; 710b57cec5SDimitry Andric // Methods that return a localized string 720b57cec5SDimitry Andric mutable llvm::SmallSet<std::pair<const IdentifierInfo *, Selector>, 12> LSM; 730b57cec5SDimitry Andric // C Functions that return a localized string 740b57cec5SDimitry Andric mutable llvm::SmallSet<const IdentifierInfo *, 5> LSF; 750b57cec5SDimitry Andric 760b57cec5SDimitry Andric void initUIMethods(ASTContext &Ctx) const; 770b57cec5SDimitry Andric void initLocStringsMethods(ASTContext &Ctx) const; 780b57cec5SDimitry Andric 790b57cec5SDimitry Andric bool hasNonLocalizedState(SVal S, CheckerContext &C) const; 800b57cec5SDimitry Andric bool hasLocalizedState(SVal S, CheckerContext &C) const; 810b57cec5SDimitry Andric void setNonLocalizedState(SVal S, CheckerContext &C) const; 820b57cec5SDimitry Andric void setLocalizedState(SVal S, CheckerContext &C) const; 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric bool isAnnotatedAsReturningLocalized(const Decl *D) const; 850b57cec5SDimitry Andric bool isAnnotatedAsTakingLocalized(const Decl *D) const; 860b57cec5SDimitry Andric void reportLocalizationError(SVal S, const CallEvent &M, CheckerContext &C, 870b57cec5SDimitry Andric int argumentNumber = 0) const; 880b57cec5SDimitry Andric 890b57cec5SDimitry Andric int getLocalizedArgumentForSelector(const IdentifierInfo *Receiver, 900b57cec5SDimitry Andric Selector S) const; 910b57cec5SDimitry Andric 920b57cec5SDimitry Andric public: 930b57cec5SDimitry Andric // When this parameter is set to true, the checker assumes all 940b57cec5SDimitry Andric // methods that return NSStrings are unlocalized. Thus, more false 950b57cec5SDimitry Andric // positives will be reported. 9681ad6265SDimitry Andric bool IsAggressive = false; 970b57cec5SDimitry Andric 980b57cec5SDimitry Andric void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 990b57cec5SDimitry Andric void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 1000b57cec5SDimitry Andric void checkPostStmt(const ObjCStringLiteral *SL, CheckerContext &C) const; 1010b57cec5SDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 1020b57cec5SDimitry Andric void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 1030b57cec5SDimitry Andric }; 1040b57cec5SDimitry Andric 1050b57cec5SDimitry Andric } // end anonymous namespace 1060b57cec5SDimitry Andric 1070b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(LocalizedMemMap, const MemRegion *, 1080b57cec5SDimitry Andric LocalizedState) 1090b57cec5SDimitry Andric 1100b57cec5SDimitry Andric namespace { 1110b57cec5SDimitry Andric class NonLocalizedStringBRVisitor final : public BugReporterVisitor { 1120b57cec5SDimitry Andric 1130b57cec5SDimitry Andric const MemRegion *NonLocalizedString; 1140b57cec5SDimitry Andric bool Satisfied; 1150b57cec5SDimitry Andric 1160b57cec5SDimitry Andric public: 1170b57cec5SDimitry Andric NonLocalizedStringBRVisitor(const MemRegion *NonLocalizedString) 1180b57cec5SDimitry Andric : NonLocalizedString(NonLocalizedString), Satisfied(false) { 1190b57cec5SDimitry Andric assert(NonLocalizedString); 1200b57cec5SDimitry Andric } 1210b57cec5SDimitry Andric 122a7dea167SDimitry Andric PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, 1230b57cec5SDimitry Andric BugReporterContext &BRC, 124a7dea167SDimitry Andric PathSensitiveBugReport &BR) override; 1250b57cec5SDimitry Andric 1260b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const override { 1270b57cec5SDimitry Andric ID.Add(NonLocalizedString); 1280b57cec5SDimitry Andric } 1290b57cec5SDimitry Andric }; 1300b57cec5SDimitry Andric } // End anonymous namespace. 1310b57cec5SDimitry Andric 1320b57cec5SDimitry Andric #define NEW_RECEIVER(receiver) \ 1330b57cec5SDimitry Andric llvm::DenseMap<Selector, uint8_t> &receiver##M = \ 1340b57cec5SDimitry Andric UIMethods.insert({&Ctx.Idents.get(#receiver), \ 1350b57cec5SDimitry Andric llvm::DenseMap<Selector, uint8_t>()}) \ 1360b57cec5SDimitry Andric .first->second; 1370b57cec5SDimitry Andric #define ADD_NULLARY_METHOD(receiver, method, argument) \ 1380b57cec5SDimitry Andric receiver##M.insert( \ 1390b57cec5SDimitry Andric {Ctx.Selectors.getNullarySelector(&Ctx.Idents.get(#method)), argument}); 1400b57cec5SDimitry Andric #define ADD_UNARY_METHOD(receiver, method, argument) \ 1410b57cec5SDimitry Andric receiver##M.insert( \ 1420b57cec5SDimitry Andric {Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(#method)), argument}); 1430b57cec5SDimitry Andric #define ADD_METHOD(receiver, method_list, count, argument) \ 1440b57cec5SDimitry Andric receiver##M.insert({Ctx.Selectors.getSelector(count, method_list), argument}); 1450b57cec5SDimitry Andric 1460b57cec5SDimitry Andric /// Initializes a list of methods that require a localized string 1470b57cec5SDimitry Andric /// Format: {"ClassName", {{"selectorName:", LocStringArg#}, ...}, ...} 1480b57cec5SDimitry Andric void NonLocalizedStringChecker::initUIMethods(ASTContext &Ctx) const { 1490b57cec5SDimitry Andric if (!UIMethods.empty()) 1500b57cec5SDimitry Andric return; 1510b57cec5SDimitry Andric 1520b57cec5SDimitry Andric // UI Methods 1530b57cec5SDimitry Andric NEW_RECEIVER(UISearchDisplayController) 1540b57cec5SDimitry Andric ADD_UNARY_METHOD(UISearchDisplayController, setSearchResultsTitle, 0) 1550b57cec5SDimitry Andric 1560b57cec5SDimitry Andric NEW_RECEIVER(UITabBarItem) 157*0fca6ea1SDimitry Andric const IdentifierInfo *initWithTitleUITabBarItemTag[] = { 1580b57cec5SDimitry Andric &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("image"), 1590b57cec5SDimitry Andric &Ctx.Idents.get("tag")}; 1600b57cec5SDimitry Andric ADD_METHOD(UITabBarItem, initWithTitleUITabBarItemTag, 3, 0) 161*0fca6ea1SDimitry Andric const IdentifierInfo *initWithTitleUITabBarItemImage[] = { 1620b57cec5SDimitry Andric &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("image"), 1630b57cec5SDimitry Andric &Ctx.Idents.get("selectedImage")}; 1640b57cec5SDimitry Andric ADD_METHOD(UITabBarItem, initWithTitleUITabBarItemImage, 3, 0) 1650b57cec5SDimitry Andric 1660b57cec5SDimitry Andric NEW_RECEIVER(NSDockTile) 1670b57cec5SDimitry Andric ADD_UNARY_METHOD(NSDockTile, setBadgeLabel, 0) 1680b57cec5SDimitry Andric 1690b57cec5SDimitry Andric NEW_RECEIVER(NSStatusItem) 1700b57cec5SDimitry Andric ADD_UNARY_METHOD(NSStatusItem, setTitle, 0) 1710b57cec5SDimitry Andric ADD_UNARY_METHOD(NSStatusItem, setToolTip, 0) 1720b57cec5SDimitry Andric 1730b57cec5SDimitry Andric NEW_RECEIVER(UITableViewRowAction) 174*0fca6ea1SDimitry Andric const IdentifierInfo *rowActionWithStyleUITableViewRowAction[] = { 1750b57cec5SDimitry Andric &Ctx.Idents.get("rowActionWithStyle"), &Ctx.Idents.get("title"), 1760b57cec5SDimitry Andric &Ctx.Idents.get("handler")}; 1770b57cec5SDimitry Andric ADD_METHOD(UITableViewRowAction, rowActionWithStyleUITableViewRowAction, 3, 1) 1780b57cec5SDimitry Andric ADD_UNARY_METHOD(UITableViewRowAction, setTitle, 0) 1790b57cec5SDimitry Andric 1800b57cec5SDimitry Andric NEW_RECEIVER(NSBox) 1810b57cec5SDimitry Andric ADD_UNARY_METHOD(NSBox, setTitle, 0) 1820b57cec5SDimitry Andric 1830b57cec5SDimitry Andric NEW_RECEIVER(NSButton) 1840b57cec5SDimitry Andric ADD_UNARY_METHOD(NSButton, setTitle, 0) 1850b57cec5SDimitry Andric ADD_UNARY_METHOD(NSButton, setAlternateTitle, 0) 186*0fca6ea1SDimitry Andric const IdentifierInfo *radioButtonWithTitleNSButton[] = { 1870b57cec5SDimitry Andric &Ctx.Idents.get("radioButtonWithTitle"), &Ctx.Idents.get("target"), 1880b57cec5SDimitry Andric &Ctx.Idents.get("action")}; 1890b57cec5SDimitry Andric ADD_METHOD(NSButton, radioButtonWithTitleNSButton, 3, 0) 190*0fca6ea1SDimitry Andric const IdentifierInfo *buttonWithTitleNSButtonImage[] = { 1910b57cec5SDimitry Andric &Ctx.Idents.get("buttonWithTitle"), &Ctx.Idents.get("image"), 1920b57cec5SDimitry Andric &Ctx.Idents.get("target"), &Ctx.Idents.get("action")}; 1930b57cec5SDimitry Andric ADD_METHOD(NSButton, buttonWithTitleNSButtonImage, 4, 0) 194*0fca6ea1SDimitry Andric const IdentifierInfo *checkboxWithTitleNSButton[] = { 1950b57cec5SDimitry Andric &Ctx.Idents.get("checkboxWithTitle"), &Ctx.Idents.get("target"), 1960b57cec5SDimitry Andric &Ctx.Idents.get("action")}; 1970b57cec5SDimitry Andric ADD_METHOD(NSButton, checkboxWithTitleNSButton, 3, 0) 198*0fca6ea1SDimitry Andric const IdentifierInfo *buttonWithTitleNSButtonTarget[] = { 1990b57cec5SDimitry Andric &Ctx.Idents.get("buttonWithTitle"), &Ctx.Idents.get("target"), 2000b57cec5SDimitry Andric &Ctx.Idents.get("action")}; 2010b57cec5SDimitry Andric ADD_METHOD(NSButton, buttonWithTitleNSButtonTarget, 3, 0) 2020b57cec5SDimitry Andric 2030b57cec5SDimitry Andric NEW_RECEIVER(NSSavePanel) 2040b57cec5SDimitry Andric ADD_UNARY_METHOD(NSSavePanel, setPrompt, 0) 2050b57cec5SDimitry Andric ADD_UNARY_METHOD(NSSavePanel, setTitle, 0) 2060b57cec5SDimitry Andric ADD_UNARY_METHOD(NSSavePanel, setNameFieldLabel, 0) 2070b57cec5SDimitry Andric ADD_UNARY_METHOD(NSSavePanel, setNameFieldStringValue, 0) 2080b57cec5SDimitry Andric ADD_UNARY_METHOD(NSSavePanel, setMessage, 0) 2090b57cec5SDimitry Andric 2100b57cec5SDimitry Andric NEW_RECEIVER(UIPrintInfo) 2110b57cec5SDimitry Andric ADD_UNARY_METHOD(UIPrintInfo, setJobName, 0) 2120b57cec5SDimitry Andric 2130b57cec5SDimitry Andric NEW_RECEIVER(NSTabViewItem) 2140b57cec5SDimitry Andric ADD_UNARY_METHOD(NSTabViewItem, setLabel, 0) 2150b57cec5SDimitry Andric ADD_UNARY_METHOD(NSTabViewItem, setToolTip, 0) 2160b57cec5SDimitry Andric 2170b57cec5SDimitry Andric NEW_RECEIVER(NSBrowser) 218*0fca6ea1SDimitry Andric const IdentifierInfo *setTitleNSBrowser[] = {&Ctx.Idents.get("setTitle"), 2190b57cec5SDimitry Andric &Ctx.Idents.get("ofColumn")}; 2200b57cec5SDimitry Andric ADD_METHOD(NSBrowser, setTitleNSBrowser, 2, 0) 2210b57cec5SDimitry Andric 2220b57cec5SDimitry Andric NEW_RECEIVER(UIAccessibilityElement) 2230b57cec5SDimitry Andric ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityLabel, 0) 2240b57cec5SDimitry Andric ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityHint, 0) 2250b57cec5SDimitry Andric ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityValue, 0) 2260b57cec5SDimitry Andric 2270b57cec5SDimitry Andric NEW_RECEIVER(UIAlertAction) 228*0fca6ea1SDimitry Andric const IdentifierInfo *actionWithTitleUIAlertAction[] = { 2290b57cec5SDimitry Andric &Ctx.Idents.get("actionWithTitle"), &Ctx.Idents.get("style"), 2300b57cec5SDimitry Andric &Ctx.Idents.get("handler")}; 2310b57cec5SDimitry Andric ADD_METHOD(UIAlertAction, actionWithTitleUIAlertAction, 3, 0) 2320b57cec5SDimitry Andric 2330b57cec5SDimitry Andric NEW_RECEIVER(NSPopUpButton) 2340b57cec5SDimitry Andric ADD_UNARY_METHOD(NSPopUpButton, addItemWithTitle, 0) 235*0fca6ea1SDimitry Andric const IdentifierInfo *insertItemWithTitleNSPopUpButton[] = { 2360b57cec5SDimitry Andric &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("atIndex")}; 2370b57cec5SDimitry Andric ADD_METHOD(NSPopUpButton, insertItemWithTitleNSPopUpButton, 2, 0) 2380b57cec5SDimitry Andric ADD_UNARY_METHOD(NSPopUpButton, removeItemWithTitle, 0) 2390b57cec5SDimitry Andric ADD_UNARY_METHOD(NSPopUpButton, selectItemWithTitle, 0) 2400b57cec5SDimitry Andric ADD_UNARY_METHOD(NSPopUpButton, setTitle, 0) 2410b57cec5SDimitry Andric 2420b57cec5SDimitry Andric NEW_RECEIVER(NSTableViewRowAction) 243*0fca6ea1SDimitry Andric const IdentifierInfo *rowActionWithStyleNSTableViewRowAction[] = { 2440b57cec5SDimitry Andric &Ctx.Idents.get("rowActionWithStyle"), &Ctx.Idents.get("title"), 2450b57cec5SDimitry Andric &Ctx.Idents.get("handler")}; 2460b57cec5SDimitry Andric ADD_METHOD(NSTableViewRowAction, rowActionWithStyleNSTableViewRowAction, 3, 1) 2470b57cec5SDimitry Andric ADD_UNARY_METHOD(NSTableViewRowAction, setTitle, 0) 2480b57cec5SDimitry Andric 2490b57cec5SDimitry Andric NEW_RECEIVER(NSImage) 2500b57cec5SDimitry Andric ADD_UNARY_METHOD(NSImage, setAccessibilityDescription, 0) 2510b57cec5SDimitry Andric 2520b57cec5SDimitry Andric NEW_RECEIVER(NSUserActivity) 2530b57cec5SDimitry Andric ADD_UNARY_METHOD(NSUserActivity, setTitle, 0) 2540b57cec5SDimitry Andric 2550b57cec5SDimitry Andric NEW_RECEIVER(NSPathControlItem) 2560b57cec5SDimitry Andric ADD_UNARY_METHOD(NSPathControlItem, setTitle, 0) 2570b57cec5SDimitry Andric 2580b57cec5SDimitry Andric NEW_RECEIVER(NSCell) 2590b57cec5SDimitry Andric ADD_UNARY_METHOD(NSCell, initTextCell, 0) 2600b57cec5SDimitry Andric ADD_UNARY_METHOD(NSCell, setTitle, 0) 2610b57cec5SDimitry Andric ADD_UNARY_METHOD(NSCell, setStringValue, 0) 2620b57cec5SDimitry Andric 2630b57cec5SDimitry Andric NEW_RECEIVER(NSPathControl) 2640b57cec5SDimitry Andric ADD_UNARY_METHOD(NSPathControl, setPlaceholderString, 0) 2650b57cec5SDimitry Andric 2660b57cec5SDimitry Andric NEW_RECEIVER(UIAccessibility) 2670b57cec5SDimitry Andric ADD_UNARY_METHOD(UIAccessibility, setAccessibilityLabel, 0) 2680b57cec5SDimitry Andric ADD_UNARY_METHOD(UIAccessibility, setAccessibilityHint, 0) 2690b57cec5SDimitry Andric ADD_UNARY_METHOD(UIAccessibility, setAccessibilityValue, 0) 2700b57cec5SDimitry Andric 2710b57cec5SDimitry Andric NEW_RECEIVER(NSTableColumn) 2720b57cec5SDimitry Andric ADD_UNARY_METHOD(NSTableColumn, setTitle, 0) 2730b57cec5SDimitry Andric ADD_UNARY_METHOD(NSTableColumn, setHeaderToolTip, 0) 2740b57cec5SDimitry Andric 2750b57cec5SDimitry Andric NEW_RECEIVER(NSSegmentedControl) 276*0fca6ea1SDimitry Andric const IdentifierInfo *setLabelNSSegmentedControl[] = { 2770b57cec5SDimitry Andric &Ctx.Idents.get("setLabel"), &Ctx.Idents.get("forSegment")}; 2780b57cec5SDimitry Andric ADD_METHOD(NSSegmentedControl, setLabelNSSegmentedControl, 2, 0) 279*0fca6ea1SDimitry Andric const IdentifierInfo *setToolTipNSSegmentedControl[] = { 2800b57cec5SDimitry Andric &Ctx.Idents.get("setToolTip"), &Ctx.Idents.get("forSegment")}; 2810b57cec5SDimitry Andric ADD_METHOD(NSSegmentedControl, setToolTipNSSegmentedControl, 2, 0) 2820b57cec5SDimitry Andric 2830b57cec5SDimitry Andric NEW_RECEIVER(NSButtonCell) 2840b57cec5SDimitry Andric ADD_UNARY_METHOD(NSButtonCell, setTitle, 0) 2850b57cec5SDimitry Andric ADD_UNARY_METHOD(NSButtonCell, setAlternateTitle, 0) 2860b57cec5SDimitry Andric 2870b57cec5SDimitry Andric NEW_RECEIVER(NSDatePickerCell) 2880b57cec5SDimitry Andric ADD_UNARY_METHOD(NSDatePickerCell, initTextCell, 0) 2890b57cec5SDimitry Andric 2900b57cec5SDimitry Andric NEW_RECEIVER(NSSliderCell) 2910b57cec5SDimitry Andric ADD_UNARY_METHOD(NSSliderCell, setTitle, 0) 2920b57cec5SDimitry Andric 2930b57cec5SDimitry Andric NEW_RECEIVER(NSControl) 2940b57cec5SDimitry Andric ADD_UNARY_METHOD(NSControl, setStringValue, 0) 2950b57cec5SDimitry Andric 2960b57cec5SDimitry Andric NEW_RECEIVER(NSAccessibility) 2970b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAccessibility, setAccessibilityValueDescription, 0) 2980b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAccessibility, setAccessibilityLabel, 0) 2990b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAccessibility, setAccessibilityTitle, 0) 3000b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAccessibility, setAccessibilityPlaceholderValue, 0) 3010b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAccessibility, setAccessibilityHelp, 0) 3020b57cec5SDimitry Andric 3030b57cec5SDimitry Andric NEW_RECEIVER(NSMatrix) 304*0fca6ea1SDimitry Andric const IdentifierInfo *setToolTipNSMatrix[] = {&Ctx.Idents.get("setToolTip"), 3050b57cec5SDimitry Andric &Ctx.Idents.get("forCell")}; 3060b57cec5SDimitry Andric ADD_METHOD(NSMatrix, setToolTipNSMatrix, 2, 0) 3070b57cec5SDimitry Andric 3080b57cec5SDimitry Andric NEW_RECEIVER(NSPrintPanel) 3090b57cec5SDimitry Andric ADD_UNARY_METHOD(NSPrintPanel, setDefaultButtonTitle, 0) 3100b57cec5SDimitry Andric 3110b57cec5SDimitry Andric NEW_RECEIVER(UILocalNotification) 3120b57cec5SDimitry Andric ADD_UNARY_METHOD(UILocalNotification, setAlertBody, 0) 3130b57cec5SDimitry Andric ADD_UNARY_METHOD(UILocalNotification, setAlertAction, 0) 3140b57cec5SDimitry Andric ADD_UNARY_METHOD(UILocalNotification, setAlertTitle, 0) 3150b57cec5SDimitry Andric 3160b57cec5SDimitry Andric NEW_RECEIVER(NSSlider) 3170b57cec5SDimitry Andric ADD_UNARY_METHOD(NSSlider, setTitle, 0) 3180b57cec5SDimitry Andric 3190b57cec5SDimitry Andric NEW_RECEIVER(UIMenuItem) 320*0fca6ea1SDimitry Andric const IdentifierInfo *initWithTitleUIMenuItem[] = { 321*0fca6ea1SDimitry Andric &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("action")}; 3220b57cec5SDimitry Andric ADD_METHOD(UIMenuItem, initWithTitleUIMenuItem, 2, 0) 3230b57cec5SDimitry Andric ADD_UNARY_METHOD(UIMenuItem, setTitle, 0) 3240b57cec5SDimitry Andric 3250b57cec5SDimitry Andric NEW_RECEIVER(UIAlertController) 326*0fca6ea1SDimitry Andric const IdentifierInfo *alertControllerWithTitleUIAlertController[] = { 3270b57cec5SDimitry Andric &Ctx.Idents.get("alertControllerWithTitle"), &Ctx.Idents.get("message"), 3280b57cec5SDimitry Andric &Ctx.Idents.get("preferredStyle")}; 3290b57cec5SDimitry Andric ADD_METHOD(UIAlertController, alertControllerWithTitleUIAlertController, 3, 1) 3300b57cec5SDimitry Andric ADD_UNARY_METHOD(UIAlertController, setTitle, 0) 3310b57cec5SDimitry Andric ADD_UNARY_METHOD(UIAlertController, setMessage, 0) 3320b57cec5SDimitry Andric 3330b57cec5SDimitry Andric NEW_RECEIVER(UIApplicationShortcutItem) 334*0fca6ea1SDimitry Andric const IdentifierInfo *initWithTypeUIApplicationShortcutItemIcon[] = { 3350b57cec5SDimitry Andric &Ctx.Idents.get("initWithType"), &Ctx.Idents.get("localizedTitle"), 3360b57cec5SDimitry Andric &Ctx.Idents.get("localizedSubtitle"), &Ctx.Idents.get("icon"), 3370b57cec5SDimitry Andric &Ctx.Idents.get("userInfo")}; 3380b57cec5SDimitry Andric ADD_METHOD(UIApplicationShortcutItem, 3390b57cec5SDimitry Andric initWithTypeUIApplicationShortcutItemIcon, 5, 1) 340*0fca6ea1SDimitry Andric const IdentifierInfo *initWithTypeUIApplicationShortcutItem[] = { 3410b57cec5SDimitry Andric &Ctx.Idents.get("initWithType"), &Ctx.Idents.get("localizedTitle")}; 3420b57cec5SDimitry Andric ADD_METHOD(UIApplicationShortcutItem, initWithTypeUIApplicationShortcutItem, 3430b57cec5SDimitry Andric 2, 1) 3440b57cec5SDimitry Andric 3450b57cec5SDimitry Andric NEW_RECEIVER(UIActionSheet) 346*0fca6ea1SDimitry Andric const IdentifierInfo *initWithTitleUIActionSheet[] = { 3470b57cec5SDimitry Andric &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("delegate"), 3480b57cec5SDimitry Andric &Ctx.Idents.get("cancelButtonTitle"), 3490b57cec5SDimitry Andric &Ctx.Idents.get("destructiveButtonTitle"), 3500b57cec5SDimitry Andric &Ctx.Idents.get("otherButtonTitles")}; 3510b57cec5SDimitry Andric ADD_METHOD(UIActionSheet, initWithTitleUIActionSheet, 5, 0) 3520b57cec5SDimitry Andric ADD_UNARY_METHOD(UIActionSheet, addButtonWithTitle, 0) 3530b57cec5SDimitry Andric ADD_UNARY_METHOD(UIActionSheet, setTitle, 0) 3540b57cec5SDimitry Andric 3550b57cec5SDimitry Andric NEW_RECEIVER(UIAccessibilityCustomAction) 356*0fca6ea1SDimitry Andric const IdentifierInfo *initWithNameUIAccessibilityCustomAction[] = { 3570b57cec5SDimitry Andric &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("target"), 3580b57cec5SDimitry Andric &Ctx.Idents.get("selector")}; 3590b57cec5SDimitry Andric ADD_METHOD(UIAccessibilityCustomAction, 3600b57cec5SDimitry Andric initWithNameUIAccessibilityCustomAction, 3, 0) 3610b57cec5SDimitry Andric ADD_UNARY_METHOD(UIAccessibilityCustomAction, setName, 0) 3620b57cec5SDimitry Andric 3630b57cec5SDimitry Andric NEW_RECEIVER(UISearchBar) 3640b57cec5SDimitry Andric ADD_UNARY_METHOD(UISearchBar, setText, 0) 3650b57cec5SDimitry Andric ADD_UNARY_METHOD(UISearchBar, setPrompt, 0) 3660b57cec5SDimitry Andric ADD_UNARY_METHOD(UISearchBar, setPlaceholder, 0) 3670b57cec5SDimitry Andric 3680b57cec5SDimitry Andric NEW_RECEIVER(UIBarItem) 3690b57cec5SDimitry Andric ADD_UNARY_METHOD(UIBarItem, setTitle, 0) 3700b57cec5SDimitry Andric 3710b57cec5SDimitry Andric NEW_RECEIVER(UITextView) 3720b57cec5SDimitry Andric ADD_UNARY_METHOD(UITextView, setText, 0) 3730b57cec5SDimitry Andric 3740b57cec5SDimitry Andric NEW_RECEIVER(NSView) 3750b57cec5SDimitry Andric ADD_UNARY_METHOD(NSView, setToolTip, 0) 3760b57cec5SDimitry Andric 3770b57cec5SDimitry Andric NEW_RECEIVER(NSTextField) 3780b57cec5SDimitry Andric ADD_UNARY_METHOD(NSTextField, setPlaceholderString, 0) 3790b57cec5SDimitry Andric ADD_UNARY_METHOD(NSTextField, textFieldWithString, 0) 3800b57cec5SDimitry Andric ADD_UNARY_METHOD(NSTextField, wrappingLabelWithString, 0) 3810b57cec5SDimitry Andric ADD_UNARY_METHOD(NSTextField, labelWithString, 0) 3820b57cec5SDimitry Andric 3830b57cec5SDimitry Andric NEW_RECEIVER(NSAttributedString) 3840b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAttributedString, initWithString, 0) 385*0fca6ea1SDimitry Andric const IdentifierInfo *initWithStringNSAttributedString[] = { 3860b57cec5SDimitry Andric &Ctx.Idents.get("initWithString"), &Ctx.Idents.get("attributes")}; 3870b57cec5SDimitry Andric ADD_METHOD(NSAttributedString, initWithStringNSAttributedString, 2, 0) 3880b57cec5SDimitry Andric 3890b57cec5SDimitry Andric NEW_RECEIVER(NSText) 3900b57cec5SDimitry Andric ADD_UNARY_METHOD(NSText, setString, 0) 3910b57cec5SDimitry Andric 3920b57cec5SDimitry Andric NEW_RECEIVER(UIKeyCommand) 393*0fca6ea1SDimitry Andric const IdentifierInfo *keyCommandWithInputUIKeyCommand[] = { 3940b57cec5SDimitry Andric &Ctx.Idents.get("keyCommandWithInput"), &Ctx.Idents.get("modifierFlags"), 3950b57cec5SDimitry Andric &Ctx.Idents.get("action"), &Ctx.Idents.get("discoverabilityTitle")}; 3960b57cec5SDimitry Andric ADD_METHOD(UIKeyCommand, keyCommandWithInputUIKeyCommand, 4, 3) 3970b57cec5SDimitry Andric ADD_UNARY_METHOD(UIKeyCommand, setDiscoverabilityTitle, 0) 3980b57cec5SDimitry Andric 3990b57cec5SDimitry Andric NEW_RECEIVER(UILabel) 4000b57cec5SDimitry Andric ADD_UNARY_METHOD(UILabel, setText, 0) 4010b57cec5SDimitry Andric 4020b57cec5SDimitry Andric NEW_RECEIVER(NSAlert) 403*0fca6ea1SDimitry Andric const IdentifierInfo *alertWithMessageTextNSAlert[] = { 4040b57cec5SDimitry Andric &Ctx.Idents.get("alertWithMessageText"), &Ctx.Idents.get("defaultButton"), 4050b57cec5SDimitry Andric &Ctx.Idents.get("alternateButton"), &Ctx.Idents.get("otherButton"), 4060b57cec5SDimitry Andric &Ctx.Idents.get("informativeTextWithFormat")}; 4070b57cec5SDimitry Andric ADD_METHOD(NSAlert, alertWithMessageTextNSAlert, 5, 0) 4080b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAlert, addButtonWithTitle, 0) 4090b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAlert, setMessageText, 0) 4100b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAlert, setInformativeText, 0) 4110b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAlert, setHelpAnchor, 0) 4120b57cec5SDimitry Andric 4130b57cec5SDimitry Andric NEW_RECEIVER(UIMutableApplicationShortcutItem) 4140b57cec5SDimitry Andric ADD_UNARY_METHOD(UIMutableApplicationShortcutItem, setLocalizedTitle, 0) 4150b57cec5SDimitry Andric ADD_UNARY_METHOD(UIMutableApplicationShortcutItem, setLocalizedSubtitle, 0) 4160b57cec5SDimitry Andric 4170b57cec5SDimitry Andric NEW_RECEIVER(UIButton) 418*0fca6ea1SDimitry Andric const IdentifierInfo *setTitleUIButton[] = {&Ctx.Idents.get("setTitle"), 4190b57cec5SDimitry Andric &Ctx.Idents.get("forState")}; 4200b57cec5SDimitry Andric ADD_METHOD(UIButton, setTitleUIButton, 2, 0) 4210b57cec5SDimitry Andric 4220b57cec5SDimitry Andric NEW_RECEIVER(NSWindow) 4230b57cec5SDimitry Andric ADD_UNARY_METHOD(NSWindow, setTitle, 0) 424*0fca6ea1SDimitry Andric const IdentifierInfo *minFrameWidthWithTitleNSWindow[] = { 4250b57cec5SDimitry Andric &Ctx.Idents.get("minFrameWidthWithTitle"), &Ctx.Idents.get("styleMask")}; 4260b57cec5SDimitry Andric ADD_METHOD(NSWindow, minFrameWidthWithTitleNSWindow, 2, 0) 4270b57cec5SDimitry Andric ADD_UNARY_METHOD(NSWindow, setMiniwindowTitle, 0) 4280b57cec5SDimitry Andric 4290b57cec5SDimitry Andric NEW_RECEIVER(NSPathCell) 4300b57cec5SDimitry Andric ADD_UNARY_METHOD(NSPathCell, setPlaceholderString, 0) 4310b57cec5SDimitry Andric 4320b57cec5SDimitry Andric NEW_RECEIVER(UIDocumentMenuViewController) 433*0fca6ea1SDimitry Andric const IdentifierInfo *addOptionWithTitleUIDocumentMenuViewController[] = { 4340b57cec5SDimitry Andric &Ctx.Idents.get("addOptionWithTitle"), &Ctx.Idents.get("image"), 4350b57cec5SDimitry Andric &Ctx.Idents.get("order"), &Ctx.Idents.get("handler")}; 4360b57cec5SDimitry Andric ADD_METHOD(UIDocumentMenuViewController, 4370b57cec5SDimitry Andric addOptionWithTitleUIDocumentMenuViewController, 4, 0) 4380b57cec5SDimitry Andric 4390b57cec5SDimitry Andric NEW_RECEIVER(UINavigationItem) 4400b57cec5SDimitry Andric ADD_UNARY_METHOD(UINavigationItem, initWithTitle, 0) 4410b57cec5SDimitry Andric ADD_UNARY_METHOD(UINavigationItem, setTitle, 0) 4420b57cec5SDimitry Andric ADD_UNARY_METHOD(UINavigationItem, setPrompt, 0) 4430b57cec5SDimitry Andric 4440b57cec5SDimitry Andric NEW_RECEIVER(UIAlertView) 445*0fca6ea1SDimitry Andric const IdentifierInfo *initWithTitleUIAlertView[] = { 4460b57cec5SDimitry Andric &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("message"), 4470b57cec5SDimitry Andric &Ctx.Idents.get("delegate"), &Ctx.Idents.get("cancelButtonTitle"), 4480b57cec5SDimitry Andric &Ctx.Idents.get("otherButtonTitles")}; 4490b57cec5SDimitry Andric ADD_METHOD(UIAlertView, initWithTitleUIAlertView, 5, 0) 4500b57cec5SDimitry Andric ADD_UNARY_METHOD(UIAlertView, addButtonWithTitle, 0) 4510b57cec5SDimitry Andric ADD_UNARY_METHOD(UIAlertView, setTitle, 0) 4520b57cec5SDimitry Andric ADD_UNARY_METHOD(UIAlertView, setMessage, 0) 4530b57cec5SDimitry Andric 4540b57cec5SDimitry Andric NEW_RECEIVER(NSFormCell) 4550b57cec5SDimitry Andric ADD_UNARY_METHOD(NSFormCell, initTextCell, 0) 4560b57cec5SDimitry Andric ADD_UNARY_METHOD(NSFormCell, setTitle, 0) 4570b57cec5SDimitry Andric ADD_UNARY_METHOD(NSFormCell, setPlaceholderString, 0) 4580b57cec5SDimitry Andric 4590b57cec5SDimitry Andric NEW_RECEIVER(NSUserNotification) 4600b57cec5SDimitry Andric ADD_UNARY_METHOD(NSUserNotification, setTitle, 0) 4610b57cec5SDimitry Andric ADD_UNARY_METHOD(NSUserNotification, setSubtitle, 0) 4620b57cec5SDimitry Andric ADD_UNARY_METHOD(NSUserNotification, setInformativeText, 0) 4630b57cec5SDimitry Andric ADD_UNARY_METHOD(NSUserNotification, setActionButtonTitle, 0) 4640b57cec5SDimitry Andric ADD_UNARY_METHOD(NSUserNotification, setOtherButtonTitle, 0) 4650b57cec5SDimitry Andric ADD_UNARY_METHOD(NSUserNotification, setResponsePlaceholder, 0) 4660b57cec5SDimitry Andric 4670b57cec5SDimitry Andric NEW_RECEIVER(NSToolbarItem) 4680b57cec5SDimitry Andric ADD_UNARY_METHOD(NSToolbarItem, setLabel, 0) 4690b57cec5SDimitry Andric ADD_UNARY_METHOD(NSToolbarItem, setPaletteLabel, 0) 4700b57cec5SDimitry Andric ADD_UNARY_METHOD(NSToolbarItem, setToolTip, 0) 4710b57cec5SDimitry Andric 4720b57cec5SDimitry Andric NEW_RECEIVER(NSProgress) 4730b57cec5SDimitry Andric ADD_UNARY_METHOD(NSProgress, setLocalizedDescription, 0) 4740b57cec5SDimitry Andric ADD_UNARY_METHOD(NSProgress, setLocalizedAdditionalDescription, 0) 4750b57cec5SDimitry Andric 4760b57cec5SDimitry Andric NEW_RECEIVER(NSSegmentedCell) 477*0fca6ea1SDimitry Andric const IdentifierInfo *setLabelNSSegmentedCell[] = { 478*0fca6ea1SDimitry Andric &Ctx.Idents.get("setLabel"), &Ctx.Idents.get("forSegment")}; 4790b57cec5SDimitry Andric ADD_METHOD(NSSegmentedCell, setLabelNSSegmentedCell, 2, 0) 480*0fca6ea1SDimitry Andric const IdentifierInfo *setToolTipNSSegmentedCell[] = { 481*0fca6ea1SDimitry Andric &Ctx.Idents.get("setToolTip"), &Ctx.Idents.get("forSegment")}; 4820b57cec5SDimitry Andric ADD_METHOD(NSSegmentedCell, setToolTipNSSegmentedCell, 2, 0) 4830b57cec5SDimitry Andric 4840b57cec5SDimitry Andric NEW_RECEIVER(NSUndoManager) 4850b57cec5SDimitry Andric ADD_UNARY_METHOD(NSUndoManager, setActionName, 0) 4860b57cec5SDimitry Andric ADD_UNARY_METHOD(NSUndoManager, undoMenuTitleForUndoActionName, 0) 4870b57cec5SDimitry Andric ADD_UNARY_METHOD(NSUndoManager, redoMenuTitleForUndoActionName, 0) 4880b57cec5SDimitry Andric 4890b57cec5SDimitry Andric NEW_RECEIVER(NSMenuItem) 490*0fca6ea1SDimitry Andric const IdentifierInfo *initWithTitleNSMenuItem[] = { 4910b57cec5SDimitry Andric &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("action"), 4920b57cec5SDimitry Andric &Ctx.Idents.get("keyEquivalent")}; 4930b57cec5SDimitry Andric ADD_METHOD(NSMenuItem, initWithTitleNSMenuItem, 3, 0) 4940b57cec5SDimitry Andric ADD_UNARY_METHOD(NSMenuItem, setTitle, 0) 4950b57cec5SDimitry Andric ADD_UNARY_METHOD(NSMenuItem, setToolTip, 0) 4960b57cec5SDimitry Andric 4970b57cec5SDimitry Andric NEW_RECEIVER(NSPopUpButtonCell) 498*0fca6ea1SDimitry Andric const IdentifierInfo *initTextCellNSPopUpButtonCell[] = { 4990b57cec5SDimitry Andric &Ctx.Idents.get("initTextCell"), &Ctx.Idents.get("pullsDown")}; 5000b57cec5SDimitry Andric ADD_METHOD(NSPopUpButtonCell, initTextCellNSPopUpButtonCell, 2, 0) 5010b57cec5SDimitry Andric ADD_UNARY_METHOD(NSPopUpButtonCell, addItemWithTitle, 0) 502*0fca6ea1SDimitry Andric const IdentifierInfo *insertItemWithTitleNSPopUpButtonCell[] = { 5030b57cec5SDimitry Andric &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("atIndex")}; 5040b57cec5SDimitry Andric ADD_METHOD(NSPopUpButtonCell, insertItemWithTitleNSPopUpButtonCell, 2, 0) 5050b57cec5SDimitry Andric ADD_UNARY_METHOD(NSPopUpButtonCell, removeItemWithTitle, 0) 5060b57cec5SDimitry Andric ADD_UNARY_METHOD(NSPopUpButtonCell, selectItemWithTitle, 0) 5070b57cec5SDimitry Andric ADD_UNARY_METHOD(NSPopUpButtonCell, setTitle, 0) 5080b57cec5SDimitry Andric 5090b57cec5SDimitry Andric NEW_RECEIVER(NSViewController) 5100b57cec5SDimitry Andric ADD_UNARY_METHOD(NSViewController, setTitle, 0) 5110b57cec5SDimitry Andric 5120b57cec5SDimitry Andric NEW_RECEIVER(NSMenu) 5130b57cec5SDimitry Andric ADD_UNARY_METHOD(NSMenu, initWithTitle, 0) 514*0fca6ea1SDimitry Andric const IdentifierInfo *insertItemWithTitleNSMenu[] = { 5150b57cec5SDimitry Andric &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("action"), 5160b57cec5SDimitry Andric &Ctx.Idents.get("keyEquivalent"), &Ctx.Idents.get("atIndex")}; 5170b57cec5SDimitry Andric ADD_METHOD(NSMenu, insertItemWithTitleNSMenu, 4, 0) 518*0fca6ea1SDimitry Andric const IdentifierInfo *addItemWithTitleNSMenu[] = { 5190b57cec5SDimitry Andric &Ctx.Idents.get("addItemWithTitle"), &Ctx.Idents.get("action"), 5200b57cec5SDimitry Andric &Ctx.Idents.get("keyEquivalent")}; 5210b57cec5SDimitry Andric ADD_METHOD(NSMenu, addItemWithTitleNSMenu, 3, 0) 5220b57cec5SDimitry Andric ADD_UNARY_METHOD(NSMenu, setTitle, 0) 5230b57cec5SDimitry Andric 5240b57cec5SDimitry Andric NEW_RECEIVER(UIMutableUserNotificationAction) 5250b57cec5SDimitry Andric ADD_UNARY_METHOD(UIMutableUserNotificationAction, setTitle, 0) 5260b57cec5SDimitry Andric 5270b57cec5SDimitry Andric NEW_RECEIVER(NSForm) 5280b57cec5SDimitry Andric ADD_UNARY_METHOD(NSForm, addEntry, 0) 529*0fca6ea1SDimitry Andric const IdentifierInfo *insertEntryNSForm[] = {&Ctx.Idents.get("insertEntry"), 5300b57cec5SDimitry Andric &Ctx.Idents.get("atIndex")}; 5310b57cec5SDimitry Andric ADD_METHOD(NSForm, insertEntryNSForm, 2, 0) 5320b57cec5SDimitry Andric 5330b57cec5SDimitry Andric NEW_RECEIVER(NSTextFieldCell) 5340b57cec5SDimitry Andric ADD_UNARY_METHOD(NSTextFieldCell, setPlaceholderString, 0) 5350b57cec5SDimitry Andric 5360b57cec5SDimitry Andric NEW_RECEIVER(NSUserNotificationAction) 537*0fca6ea1SDimitry Andric const IdentifierInfo *actionWithIdentifierNSUserNotificationAction[] = { 5380b57cec5SDimitry Andric &Ctx.Idents.get("actionWithIdentifier"), &Ctx.Idents.get("title")}; 5390b57cec5SDimitry Andric ADD_METHOD(NSUserNotificationAction, 5400b57cec5SDimitry Andric actionWithIdentifierNSUserNotificationAction, 2, 1) 5410b57cec5SDimitry Andric 5420b57cec5SDimitry Andric NEW_RECEIVER(UITextField) 5430b57cec5SDimitry Andric ADD_UNARY_METHOD(UITextField, setText, 0) 5440b57cec5SDimitry Andric ADD_UNARY_METHOD(UITextField, setPlaceholder, 0) 5450b57cec5SDimitry Andric 5460b57cec5SDimitry Andric NEW_RECEIVER(UIBarButtonItem) 547*0fca6ea1SDimitry Andric const IdentifierInfo *initWithTitleUIBarButtonItem[] = { 5480b57cec5SDimitry Andric &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("style"), 5490b57cec5SDimitry Andric &Ctx.Idents.get("target"), &Ctx.Idents.get("action")}; 5500b57cec5SDimitry Andric ADD_METHOD(UIBarButtonItem, initWithTitleUIBarButtonItem, 4, 0) 5510b57cec5SDimitry Andric 5520b57cec5SDimitry Andric NEW_RECEIVER(UIViewController) 5530b57cec5SDimitry Andric ADD_UNARY_METHOD(UIViewController, setTitle, 0) 5540b57cec5SDimitry Andric 5550b57cec5SDimitry Andric NEW_RECEIVER(UISegmentedControl) 556*0fca6ea1SDimitry Andric const IdentifierInfo *insertSegmentWithTitleUISegmentedControl[] = { 5570b57cec5SDimitry Andric &Ctx.Idents.get("insertSegmentWithTitle"), &Ctx.Idents.get("atIndex"), 5580b57cec5SDimitry Andric &Ctx.Idents.get("animated")}; 5590b57cec5SDimitry Andric ADD_METHOD(UISegmentedControl, insertSegmentWithTitleUISegmentedControl, 3, 0) 560*0fca6ea1SDimitry Andric const IdentifierInfo *setTitleUISegmentedControl[] = { 5610b57cec5SDimitry Andric &Ctx.Idents.get("setTitle"), &Ctx.Idents.get("forSegmentAtIndex")}; 5620b57cec5SDimitry Andric ADD_METHOD(UISegmentedControl, setTitleUISegmentedControl, 2, 0) 5630b57cec5SDimitry Andric 5640b57cec5SDimitry Andric NEW_RECEIVER(NSAccessibilityCustomRotorItemResult) 565*0fca6ea1SDimitry Andric const IdentifierInfo 5660b57cec5SDimitry Andric *initWithItemLoadingTokenNSAccessibilityCustomRotorItemResult[] = { 5670b57cec5SDimitry Andric &Ctx.Idents.get("initWithItemLoadingToken"), 5680b57cec5SDimitry Andric &Ctx.Idents.get("customLabel")}; 5690b57cec5SDimitry Andric ADD_METHOD(NSAccessibilityCustomRotorItemResult, 5700b57cec5SDimitry Andric initWithItemLoadingTokenNSAccessibilityCustomRotorItemResult, 2, 1) 5710b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAccessibilityCustomRotorItemResult, setCustomLabel, 0) 5720b57cec5SDimitry Andric 5730b57cec5SDimitry Andric NEW_RECEIVER(UIContextualAction) 574*0fca6ea1SDimitry Andric const IdentifierInfo *contextualActionWithStyleUIContextualAction[] = { 5750b57cec5SDimitry Andric &Ctx.Idents.get("contextualActionWithStyle"), &Ctx.Idents.get("title"), 5760b57cec5SDimitry Andric &Ctx.Idents.get("handler")}; 5770b57cec5SDimitry Andric ADD_METHOD(UIContextualAction, contextualActionWithStyleUIContextualAction, 3, 5780b57cec5SDimitry Andric 1) 5790b57cec5SDimitry Andric ADD_UNARY_METHOD(UIContextualAction, setTitle, 0) 5800b57cec5SDimitry Andric 5810b57cec5SDimitry Andric NEW_RECEIVER(NSAccessibilityCustomRotor) 582*0fca6ea1SDimitry Andric const IdentifierInfo *initWithLabelNSAccessibilityCustomRotor[] = { 5830b57cec5SDimitry Andric &Ctx.Idents.get("initWithLabel"), &Ctx.Idents.get("itemSearchDelegate")}; 5840b57cec5SDimitry Andric ADD_METHOD(NSAccessibilityCustomRotor, 5850b57cec5SDimitry Andric initWithLabelNSAccessibilityCustomRotor, 2, 0) 5860b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAccessibilityCustomRotor, setLabel, 0) 5870b57cec5SDimitry Andric 5880b57cec5SDimitry Andric NEW_RECEIVER(NSWindowTab) 5890b57cec5SDimitry Andric ADD_UNARY_METHOD(NSWindowTab, setTitle, 0) 5900b57cec5SDimitry Andric ADD_UNARY_METHOD(NSWindowTab, setToolTip, 0) 5910b57cec5SDimitry Andric 5920b57cec5SDimitry Andric NEW_RECEIVER(NSAccessibilityCustomAction) 593*0fca6ea1SDimitry Andric const IdentifierInfo *initWithNameNSAccessibilityCustomAction[] = { 5940b57cec5SDimitry Andric &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("handler")}; 5950b57cec5SDimitry Andric ADD_METHOD(NSAccessibilityCustomAction, 5960b57cec5SDimitry Andric initWithNameNSAccessibilityCustomAction, 2, 0) 597*0fca6ea1SDimitry Andric const IdentifierInfo *initWithNameTargetNSAccessibilityCustomAction[] = { 5980b57cec5SDimitry Andric &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("target"), 5990b57cec5SDimitry Andric &Ctx.Idents.get("selector")}; 6000b57cec5SDimitry Andric ADD_METHOD(NSAccessibilityCustomAction, 6010b57cec5SDimitry Andric initWithNameTargetNSAccessibilityCustomAction, 3, 0) 6020b57cec5SDimitry Andric ADD_UNARY_METHOD(NSAccessibilityCustomAction, setName, 0) 6030b57cec5SDimitry Andric } 6040b57cec5SDimitry Andric 6050b57cec5SDimitry Andric #define LSF_INSERT(function_name) LSF.insert(&Ctx.Idents.get(function_name)); 6060b57cec5SDimitry Andric #define LSM_INSERT_NULLARY(receiver, method_name) \ 6070b57cec5SDimitry Andric LSM.insert({&Ctx.Idents.get(receiver), Ctx.Selectors.getNullarySelector( \ 6080b57cec5SDimitry Andric &Ctx.Idents.get(method_name))}); 6090b57cec5SDimitry Andric #define LSM_INSERT_UNARY(receiver, method_name) \ 6100b57cec5SDimitry Andric LSM.insert({&Ctx.Idents.get(receiver), \ 6110b57cec5SDimitry Andric Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(method_name))}); 6120b57cec5SDimitry Andric #define LSM_INSERT_SELECTOR(receiver, method_list, arguments) \ 6130b57cec5SDimitry Andric LSM.insert({&Ctx.Idents.get(receiver), \ 6140b57cec5SDimitry Andric Ctx.Selectors.getSelector(arguments, method_list)}); 6150b57cec5SDimitry Andric 6160b57cec5SDimitry Andric /// Initializes a list of methods and C functions that return a localized string 6170b57cec5SDimitry Andric void NonLocalizedStringChecker::initLocStringsMethods(ASTContext &Ctx) const { 6180b57cec5SDimitry Andric if (!LSM.empty()) 6190b57cec5SDimitry Andric return; 6200b57cec5SDimitry Andric 621*0fca6ea1SDimitry Andric const IdentifierInfo *LocalizedStringMacro[] = { 6220b57cec5SDimitry Andric &Ctx.Idents.get("localizedStringForKey"), &Ctx.Idents.get("value"), 6230b57cec5SDimitry Andric &Ctx.Idents.get("table")}; 6240b57cec5SDimitry Andric LSM_INSERT_SELECTOR("NSBundle", LocalizedStringMacro, 3) 6250b57cec5SDimitry Andric LSM_INSERT_UNARY("NSDateFormatter", "stringFromDate") 626*0fca6ea1SDimitry Andric const IdentifierInfo *LocalizedStringFromDate[] = { 6270b57cec5SDimitry Andric &Ctx.Idents.get("localizedStringFromDate"), &Ctx.Idents.get("dateStyle"), 6280b57cec5SDimitry Andric &Ctx.Idents.get("timeStyle")}; 6290b57cec5SDimitry Andric LSM_INSERT_SELECTOR("NSDateFormatter", LocalizedStringFromDate, 3) 6300b57cec5SDimitry Andric LSM_INSERT_UNARY("NSNumberFormatter", "stringFromNumber") 6310b57cec5SDimitry Andric LSM_INSERT_NULLARY("UITextField", "text") 6320b57cec5SDimitry Andric LSM_INSERT_NULLARY("UITextView", "text") 6330b57cec5SDimitry Andric LSM_INSERT_NULLARY("UILabel", "text") 6340b57cec5SDimitry Andric 6350b57cec5SDimitry Andric LSF_INSERT("CFDateFormatterCreateStringWithDate"); 6360b57cec5SDimitry Andric LSF_INSERT("CFDateFormatterCreateStringWithAbsoluteTime"); 6370b57cec5SDimitry Andric LSF_INSERT("CFNumberFormatterCreateStringWithNumber"); 6380b57cec5SDimitry Andric } 6390b57cec5SDimitry Andric 6400b57cec5SDimitry Andric /// Checks to see if the method / function declaration includes 6410b57cec5SDimitry Andric /// __attribute__((annotate("returns_localized_nsstring"))) 6420b57cec5SDimitry Andric bool NonLocalizedStringChecker::isAnnotatedAsReturningLocalized( 6430b57cec5SDimitry Andric const Decl *D) const { 6440b57cec5SDimitry Andric if (!D) 6450b57cec5SDimitry Andric return false; 6460b57cec5SDimitry Andric return std::any_of( 6470b57cec5SDimitry Andric D->specific_attr_begin<AnnotateAttr>(), 6480b57cec5SDimitry Andric D->specific_attr_end<AnnotateAttr>(), [](const AnnotateAttr *Ann) { 6490b57cec5SDimitry Andric return Ann->getAnnotation() == "returns_localized_nsstring"; 6500b57cec5SDimitry Andric }); 6510b57cec5SDimitry Andric } 6520b57cec5SDimitry Andric 6530b57cec5SDimitry Andric /// Checks to see if the method / function declaration includes 6540b57cec5SDimitry Andric /// __attribute__((annotate("takes_localized_nsstring"))) 6550b57cec5SDimitry Andric bool NonLocalizedStringChecker::isAnnotatedAsTakingLocalized( 6560b57cec5SDimitry Andric const Decl *D) const { 6570b57cec5SDimitry Andric if (!D) 6580b57cec5SDimitry Andric return false; 6590b57cec5SDimitry Andric return std::any_of( 6600b57cec5SDimitry Andric D->specific_attr_begin<AnnotateAttr>(), 6610b57cec5SDimitry Andric D->specific_attr_end<AnnotateAttr>(), [](const AnnotateAttr *Ann) { 6620b57cec5SDimitry Andric return Ann->getAnnotation() == "takes_localized_nsstring"; 6630b57cec5SDimitry Andric }); 6640b57cec5SDimitry Andric } 6650b57cec5SDimitry Andric 6660b57cec5SDimitry Andric /// Returns true if the given SVal is marked as Localized in the program state 6670b57cec5SDimitry Andric bool NonLocalizedStringChecker::hasLocalizedState(SVal S, 6680b57cec5SDimitry Andric CheckerContext &C) const { 6690b57cec5SDimitry Andric const MemRegion *mt = S.getAsRegion(); 6700b57cec5SDimitry Andric if (mt) { 6710b57cec5SDimitry Andric const LocalizedState *LS = C.getState()->get<LocalizedMemMap>(mt); 6720b57cec5SDimitry Andric if (LS && LS->isLocalized()) 6730b57cec5SDimitry Andric return true; 6740b57cec5SDimitry Andric } 6750b57cec5SDimitry Andric return false; 6760b57cec5SDimitry Andric } 6770b57cec5SDimitry Andric 6780b57cec5SDimitry Andric /// Returns true if the given SVal is marked as NonLocalized in the program 6790b57cec5SDimitry Andric /// state 6800b57cec5SDimitry Andric bool NonLocalizedStringChecker::hasNonLocalizedState(SVal S, 6810b57cec5SDimitry Andric CheckerContext &C) const { 6820b57cec5SDimitry Andric const MemRegion *mt = S.getAsRegion(); 6830b57cec5SDimitry Andric if (mt) { 6840b57cec5SDimitry Andric const LocalizedState *LS = C.getState()->get<LocalizedMemMap>(mt); 6850b57cec5SDimitry Andric if (LS && LS->isNonLocalized()) 6860b57cec5SDimitry Andric return true; 6870b57cec5SDimitry Andric } 6880b57cec5SDimitry Andric return false; 6890b57cec5SDimitry Andric } 6900b57cec5SDimitry Andric 6910b57cec5SDimitry Andric /// Marks the given SVal as Localized in the program state 6920b57cec5SDimitry Andric void NonLocalizedStringChecker::setLocalizedState(const SVal S, 6930b57cec5SDimitry Andric CheckerContext &C) const { 6940b57cec5SDimitry Andric const MemRegion *mt = S.getAsRegion(); 6950b57cec5SDimitry Andric if (mt) { 6960b57cec5SDimitry Andric ProgramStateRef State = 6970b57cec5SDimitry Andric C.getState()->set<LocalizedMemMap>(mt, LocalizedState::getLocalized()); 6980b57cec5SDimitry Andric C.addTransition(State); 6990b57cec5SDimitry Andric } 7000b57cec5SDimitry Andric } 7010b57cec5SDimitry Andric 7020b57cec5SDimitry Andric /// Marks the given SVal as NonLocalized in the program state 7030b57cec5SDimitry Andric void NonLocalizedStringChecker::setNonLocalizedState(const SVal S, 7040b57cec5SDimitry Andric CheckerContext &C) const { 7050b57cec5SDimitry Andric const MemRegion *mt = S.getAsRegion(); 7060b57cec5SDimitry Andric if (mt) { 7070b57cec5SDimitry Andric ProgramStateRef State = C.getState()->set<LocalizedMemMap>( 7080b57cec5SDimitry Andric mt, LocalizedState::getNonLocalized()); 7090b57cec5SDimitry Andric C.addTransition(State); 7100b57cec5SDimitry Andric } 7110b57cec5SDimitry Andric } 7120b57cec5SDimitry Andric 7130b57cec5SDimitry Andric 7140b57cec5SDimitry Andric static bool isDebuggingName(std::string name) { 7157a6dacacSDimitry Andric return StringRef(name).contains_insensitive("debug"); 7160b57cec5SDimitry Andric } 7170b57cec5SDimitry Andric 7180b57cec5SDimitry Andric /// Returns true when, heuristically, the analyzer may be analyzing debugging 7190b57cec5SDimitry Andric /// code. We use this to suppress localization diagnostics in un-localized user 7200b57cec5SDimitry Andric /// interfaces that are only used for debugging and are therefore not user 7210b57cec5SDimitry Andric /// facing. 7220b57cec5SDimitry Andric static bool isDebuggingContext(CheckerContext &C) { 7230b57cec5SDimitry Andric const Decl *D = C.getCurrentAnalysisDeclContext()->getDecl(); 7240b57cec5SDimitry Andric if (!D) 7250b57cec5SDimitry Andric return false; 7260b57cec5SDimitry Andric 7270b57cec5SDimitry Andric if (auto *ND = dyn_cast<NamedDecl>(D)) { 7280b57cec5SDimitry Andric if (isDebuggingName(ND->getNameAsString())) 7290b57cec5SDimitry Andric return true; 7300b57cec5SDimitry Andric } 7310b57cec5SDimitry Andric 7320b57cec5SDimitry Andric const DeclContext *DC = D->getDeclContext(); 7330b57cec5SDimitry Andric 7340b57cec5SDimitry Andric if (auto *CD = dyn_cast<ObjCContainerDecl>(DC)) { 7350b57cec5SDimitry Andric if (isDebuggingName(CD->getNameAsString())) 7360b57cec5SDimitry Andric return true; 7370b57cec5SDimitry Andric } 7380b57cec5SDimitry Andric 7390b57cec5SDimitry Andric return false; 7400b57cec5SDimitry Andric } 7410b57cec5SDimitry Andric 7420b57cec5SDimitry Andric 7430b57cec5SDimitry Andric /// Reports a localization error for the passed in method call and SVal 7440b57cec5SDimitry Andric void NonLocalizedStringChecker::reportLocalizationError( 7450b57cec5SDimitry Andric SVal S, const CallEvent &M, CheckerContext &C, int argumentNumber) const { 7460b57cec5SDimitry Andric 7470b57cec5SDimitry Andric // Don't warn about localization errors in classes and methods that 7480b57cec5SDimitry Andric // may be debug code. 7490b57cec5SDimitry Andric if (isDebuggingContext(C)) 7500b57cec5SDimitry Andric return; 7510b57cec5SDimitry Andric 7520b57cec5SDimitry Andric static CheckerProgramPointTag Tag("NonLocalizedStringChecker", 7530b57cec5SDimitry Andric "UnlocalizedString"); 7540b57cec5SDimitry Andric ExplodedNode *ErrNode = C.addTransition(C.getState(), C.getPredecessor(), &Tag); 7550b57cec5SDimitry Andric 7560b57cec5SDimitry Andric if (!ErrNode) 7570b57cec5SDimitry Andric return; 7580b57cec5SDimitry Andric 7590b57cec5SDimitry Andric // Generate the bug report. 760a7dea167SDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>( 761647cbc5dSDimitry Andric BT, "User-facing text should use localized string macro", ErrNode); 7620b57cec5SDimitry Andric if (argumentNumber) { 7630b57cec5SDimitry Andric R->addRange(M.getArgExpr(argumentNumber - 1)->getSourceRange()); 7640b57cec5SDimitry Andric } else { 7650b57cec5SDimitry Andric R->addRange(M.getSourceRange()); 7660b57cec5SDimitry Andric } 7670b57cec5SDimitry Andric R->markInteresting(S); 7680b57cec5SDimitry Andric 7690b57cec5SDimitry Andric const MemRegion *StringRegion = S.getAsRegion(); 7700b57cec5SDimitry Andric if (StringRegion) 771a7dea167SDimitry Andric R->addVisitor(std::make_unique<NonLocalizedStringBRVisitor>(StringRegion)); 7720b57cec5SDimitry Andric 7730b57cec5SDimitry Andric C.emitReport(std::move(R)); 7740b57cec5SDimitry Andric } 7750b57cec5SDimitry Andric 7760b57cec5SDimitry Andric /// Returns the argument number requiring localized string if it exists 7770b57cec5SDimitry Andric /// otherwise, returns -1 7780b57cec5SDimitry Andric int NonLocalizedStringChecker::getLocalizedArgumentForSelector( 7790b57cec5SDimitry Andric const IdentifierInfo *Receiver, Selector S) const { 7800b57cec5SDimitry Andric auto method = UIMethods.find(Receiver); 7810b57cec5SDimitry Andric 7820b57cec5SDimitry Andric if (method == UIMethods.end()) 7830b57cec5SDimitry Andric return -1; 7840b57cec5SDimitry Andric 7850b57cec5SDimitry Andric auto argumentIterator = method->getSecond().find(S); 7860b57cec5SDimitry Andric 7870b57cec5SDimitry Andric if (argumentIterator == method->getSecond().end()) 7880b57cec5SDimitry Andric return -1; 7890b57cec5SDimitry Andric 7900b57cec5SDimitry Andric int argumentNumber = argumentIterator->getSecond(); 7910b57cec5SDimitry Andric return argumentNumber; 7920b57cec5SDimitry Andric } 7930b57cec5SDimitry Andric 7940b57cec5SDimitry Andric /// Check if the string being passed in has NonLocalized state 7950b57cec5SDimitry Andric void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 7960b57cec5SDimitry Andric CheckerContext &C) const { 7970b57cec5SDimitry Andric initUIMethods(C.getASTContext()); 7980b57cec5SDimitry Andric 7990b57cec5SDimitry Andric const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); 8000b57cec5SDimitry Andric if (!OD) 8010b57cec5SDimitry Andric return; 8020b57cec5SDimitry Andric const IdentifierInfo *odInfo = OD->getIdentifier(); 8030b57cec5SDimitry Andric 8040b57cec5SDimitry Andric Selector S = msg.getSelector(); 8050b57cec5SDimitry Andric 8060b57cec5SDimitry Andric std::string SelectorString = S.getAsString(); 8070b57cec5SDimitry Andric StringRef SelectorName = SelectorString; 8080b57cec5SDimitry Andric assert(!SelectorName.empty()); 8090b57cec5SDimitry Andric 8100b57cec5SDimitry Andric if (odInfo->isStr("NSString")) { 8110b57cec5SDimitry Andric // Handle the case where the receiver is an NSString 8120b57cec5SDimitry Andric // These special NSString methods draw to the screen 8130b57cec5SDimitry Andric 8145f757f3fSDimitry Andric if (!(SelectorName.starts_with("drawAtPoint") || 8155f757f3fSDimitry Andric SelectorName.starts_with("drawInRect") || 8165f757f3fSDimitry Andric SelectorName.starts_with("drawWithRect"))) 8170b57cec5SDimitry Andric return; 8180b57cec5SDimitry Andric 8190b57cec5SDimitry Andric SVal svTitle = msg.getReceiverSVal(); 8200b57cec5SDimitry Andric 8210b57cec5SDimitry Andric bool isNonLocalized = hasNonLocalizedState(svTitle, C); 8220b57cec5SDimitry Andric 8230b57cec5SDimitry Andric if (isNonLocalized) { 8240b57cec5SDimitry Andric reportLocalizationError(svTitle, msg, C); 8250b57cec5SDimitry Andric } 8260b57cec5SDimitry Andric } 8270b57cec5SDimitry Andric 8280b57cec5SDimitry Andric int argumentNumber = getLocalizedArgumentForSelector(odInfo, S); 8290b57cec5SDimitry Andric // Go up each hierarchy of superclasses and their protocols 8300b57cec5SDimitry Andric while (argumentNumber < 0 && OD->getSuperClass() != nullptr) { 8310b57cec5SDimitry Andric for (const auto *P : OD->all_referenced_protocols()) { 8320b57cec5SDimitry Andric argumentNumber = getLocalizedArgumentForSelector(P->getIdentifier(), S); 8330b57cec5SDimitry Andric if (argumentNumber >= 0) 8340b57cec5SDimitry Andric break; 8350b57cec5SDimitry Andric } 8360b57cec5SDimitry Andric if (argumentNumber < 0) { 8370b57cec5SDimitry Andric OD = OD->getSuperClass(); 8380b57cec5SDimitry Andric argumentNumber = getLocalizedArgumentForSelector(OD->getIdentifier(), S); 8390b57cec5SDimitry Andric } 8400b57cec5SDimitry Andric } 8410b57cec5SDimitry Andric 8420b57cec5SDimitry Andric if (argumentNumber < 0) { // There was no match in UIMethods 8430b57cec5SDimitry Andric if (const Decl *D = msg.getDecl()) { 8440b57cec5SDimitry Andric if (const ObjCMethodDecl *OMD = dyn_cast_or_null<ObjCMethodDecl>(D)) { 84506c3fb27SDimitry Andric for (auto [Idx, FormalParam] : llvm::enumerate(OMD->parameters())) { 84606c3fb27SDimitry Andric if (isAnnotatedAsTakingLocalized(FormalParam)) { 84706c3fb27SDimitry Andric argumentNumber = Idx; 8480b57cec5SDimitry Andric break; 8490b57cec5SDimitry Andric } 8500b57cec5SDimitry Andric } 8510b57cec5SDimitry Andric } 8520b57cec5SDimitry Andric } 8530b57cec5SDimitry Andric } 8540b57cec5SDimitry Andric 8550b57cec5SDimitry Andric if (argumentNumber < 0) // Still no match 8560b57cec5SDimitry Andric return; 8570b57cec5SDimitry Andric 8580b57cec5SDimitry Andric SVal svTitle = msg.getArgSVal(argumentNumber); 8590b57cec5SDimitry Andric 8600b57cec5SDimitry Andric if (const ObjCStringRegion *SR = 8610b57cec5SDimitry Andric dyn_cast_or_null<ObjCStringRegion>(svTitle.getAsRegion())) { 8620b57cec5SDimitry Andric StringRef stringValue = 8630b57cec5SDimitry Andric SR->getObjCStringLiteral()->getString()->getString(); 8640b57cec5SDimitry Andric if ((stringValue.trim().size() == 0 && stringValue.size() > 0) || 8650b57cec5SDimitry Andric stringValue.empty()) 8660b57cec5SDimitry Andric return; 8670b57cec5SDimitry Andric if (!IsAggressive && llvm::sys::unicode::columnWidthUTF8(stringValue) < 2) 8680b57cec5SDimitry Andric return; 8690b57cec5SDimitry Andric } 8700b57cec5SDimitry Andric 8710b57cec5SDimitry Andric bool isNonLocalized = hasNonLocalizedState(svTitle, C); 8720b57cec5SDimitry Andric 8730b57cec5SDimitry Andric if (isNonLocalized) { 8740b57cec5SDimitry Andric reportLocalizationError(svTitle, msg, C, argumentNumber + 1); 8750b57cec5SDimitry Andric } 8760b57cec5SDimitry Andric } 8770b57cec5SDimitry Andric 8780b57cec5SDimitry Andric void NonLocalizedStringChecker::checkPreCall(const CallEvent &Call, 8790b57cec5SDimitry Andric CheckerContext &C) const { 880a7dea167SDimitry Andric const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 881a7dea167SDimitry Andric if (!FD) 882a7dea167SDimitry Andric return; 883a7dea167SDimitry Andric 8840b57cec5SDimitry Andric auto formals = FD->parameters(); 885a7dea167SDimitry Andric for (unsigned i = 0, ei = std::min(static_cast<unsigned>(formals.size()), 886a7dea167SDimitry Andric Call.getNumArgs()); i != ei; ++i) { 8870b57cec5SDimitry Andric if (isAnnotatedAsTakingLocalized(formals[i])) { 8880b57cec5SDimitry Andric auto actual = Call.getArgSVal(i); 8890b57cec5SDimitry Andric if (hasNonLocalizedState(actual, C)) { 8900b57cec5SDimitry Andric reportLocalizationError(actual, Call, C, i + 1); 8910b57cec5SDimitry Andric } 8920b57cec5SDimitry Andric } 8930b57cec5SDimitry Andric } 8940b57cec5SDimitry Andric } 8950b57cec5SDimitry Andric 8960b57cec5SDimitry Andric static inline bool isNSStringType(QualType T, ASTContext &Ctx) { 8970b57cec5SDimitry Andric 8980b57cec5SDimitry Andric const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); 8990b57cec5SDimitry Andric if (!PT) 9000b57cec5SDimitry Andric return false; 9010b57cec5SDimitry Andric 9020b57cec5SDimitry Andric ObjCInterfaceDecl *Cls = PT->getObjectType()->getInterface(); 9030b57cec5SDimitry Andric if (!Cls) 9040b57cec5SDimitry Andric return false; 9050b57cec5SDimitry Andric 906*0fca6ea1SDimitry Andric const IdentifierInfo *ClsName = Cls->getIdentifier(); 9070b57cec5SDimitry Andric 9080b57cec5SDimitry Andric // FIXME: Should we walk the chain of classes? 9090b57cec5SDimitry Andric return ClsName == &Ctx.Idents.get("NSString") || 9100b57cec5SDimitry Andric ClsName == &Ctx.Idents.get("NSMutableString"); 9110b57cec5SDimitry Andric } 9120b57cec5SDimitry Andric 9130b57cec5SDimitry Andric /// Marks a string being returned by any call as localized 9140b57cec5SDimitry Andric /// if it is in LocStringFunctions (LSF) or the function is annotated. 9150b57cec5SDimitry Andric /// Otherwise, we mark it as NonLocalized (Aggressive) or 9160b57cec5SDimitry Andric /// NonLocalized only if it is not backed by a SymRegion (Non-Aggressive), 9170b57cec5SDimitry Andric /// basically leaving only string literals as NonLocalized. 9180b57cec5SDimitry Andric void NonLocalizedStringChecker::checkPostCall(const CallEvent &Call, 9190b57cec5SDimitry Andric CheckerContext &C) const { 9200b57cec5SDimitry Andric initLocStringsMethods(C.getASTContext()); 9210b57cec5SDimitry Andric 9220b57cec5SDimitry Andric if (!Call.getOriginExpr()) 9230b57cec5SDimitry Andric return; 9240b57cec5SDimitry Andric 9250b57cec5SDimitry Andric // Anything that takes in a localized NSString as an argument 9260b57cec5SDimitry Andric // and returns an NSString will be assumed to be returning a 9270b57cec5SDimitry Andric // localized NSString. (Counter: Incorrectly combining two LocalizedStrings) 9280b57cec5SDimitry Andric const QualType RT = Call.getResultType(); 9290b57cec5SDimitry Andric if (isNSStringType(RT, C.getASTContext())) { 9300b57cec5SDimitry Andric for (unsigned i = 0; i < Call.getNumArgs(); ++i) { 9310b57cec5SDimitry Andric SVal argValue = Call.getArgSVal(i); 9320b57cec5SDimitry Andric if (hasLocalizedState(argValue, C)) { 9330b57cec5SDimitry Andric SVal sv = Call.getReturnValue(); 9340b57cec5SDimitry Andric setLocalizedState(sv, C); 9350b57cec5SDimitry Andric return; 9360b57cec5SDimitry Andric } 9370b57cec5SDimitry Andric } 9380b57cec5SDimitry Andric } 9390b57cec5SDimitry Andric 9400b57cec5SDimitry Andric const Decl *D = Call.getDecl(); 9410b57cec5SDimitry Andric if (!D) 9420b57cec5SDimitry Andric return; 9430b57cec5SDimitry Andric 9440b57cec5SDimitry Andric const IdentifierInfo *Identifier = Call.getCalleeIdentifier(); 9450b57cec5SDimitry Andric 9460b57cec5SDimitry Andric SVal sv = Call.getReturnValue(); 947349cc55cSDimitry Andric if (isAnnotatedAsReturningLocalized(D) || LSF.contains(Identifier)) { 9480b57cec5SDimitry Andric setLocalizedState(sv, C); 9490b57cec5SDimitry Andric } else if (isNSStringType(RT, C.getASTContext()) && 9500b57cec5SDimitry Andric !hasLocalizedState(sv, C)) { 9510b57cec5SDimitry Andric if (IsAggressive) { 9520b57cec5SDimitry Andric setNonLocalizedState(sv, C); 9530b57cec5SDimitry Andric } else { 9540b57cec5SDimitry Andric const SymbolicRegion *SymReg = 9550b57cec5SDimitry Andric dyn_cast_or_null<SymbolicRegion>(sv.getAsRegion()); 9560b57cec5SDimitry Andric if (!SymReg) 9570b57cec5SDimitry Andric setNonLocalizedState(sv, C); 9580b57cec5SDimitry Andric } 9590b57cec5SDimitry Andric } 9600b57cec5SDimitry Andric } 9610b57cec5SDimitry Andric 9620b57cec5SDimitry Andric /// Marks a string being returned by an ObjC method as localized 9630b57cec5SDimitry Andric /// if it is in LocStringMethods or the method is annotated 9640b57cec5SDimitry Andric void NonLocalizedStringChecker::checkPostObjCMessage(const ObjCMethodCall &msg, 9650b57cec5SDimitry Andric CheckerContext &C) const { 9660b57cec5SDimitry Andric initLocStringsMethods(C.getASTContext()); 9670b57cec5SDimitry Andric 9680b57cec5SDimitry Andric if (!msg.isInstanceMessage()) 9690b57cec5SDimitry Andric return; 9700b57cec5SDimitry Andric 9710b57cec5SDimitry Andric const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); 9720b57cec5SDimitry Andric if (!OD) 9730b57cec5SDimitry Andric return; 9740b57cec5SDimitry Andric const IdentifierInfo *odInfo = OD->getIdentifier(); 9750b57cec5SDimitry Andric 9760b57cec5SDimitry Andric Selector S = msg.getSelector(); 9770b57cec5SDimitry Andric std::string SelectorName = S.getAsString(); 9780b57cec5SDimitry Andric 9790b57cec5SDimitry Andric std::pair<const IdentifierInfo *, Selector> MethodDescription = {odInfo, S}; 9800b57cec5SDimitry Andric 9810b57cec5SDimitry Andric if (LSM.count(MethodDescription) || 9820b57cec5SDimitry Andric isAnnotatedAsReturningLocalized(msg.getDecl())) { 9830b57cec5SDimitry Andric SVal sv = msg.getReturnValue(); 9840b57cec5SDimitry Andric setLocalizedState(sv, C); 9850b57cec5SDimitry Andric } 9860b57cec5SDimitry Andric } 9870b57cec5SDimitry Andric 9880b57cec5SDimitry Andric /// Marks all empty string literals as localized 9890b57cec5SDimitry Andric void NonLocalizedStringChecker::checkPostStmt(const ObjCStringLiteral *SL, 9900b57cec5SDimitry Andric CheckerContext &C) const { 9910b57cec5SDimitry Andric SVal sv = C.getSVal(SL); 9920b57cec5SDimitry Andric setNonLocalizedState(sv, C); 9930b57cec5SDimitry Andric } 9940b57cec5SDimitry Andric 995a7dea167SDimitry Andric PathDiagnosticPieceRef 9960b57cec5SDimitry Andric NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ, 997a7dea167SDimitry Andric BugReporterContext &BRC, 998a7dea167SDimitry Andric PathSensitiveBugReport &BR) { 9990b57cec5SDimitry Andric if (Satisfied) 10000b57cec5SDimitry Andric return nullptr; 10010b57cec5SDimitry Andric 1002bdd1243dSDimitry Andric std::optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>(); 100381ad6265SDimitry Andric if (!Point) 10040b57cec5SDimitry Andric return nullptr; 10050b57cec5SDimitry Andric 10060b57cec5SDimitry Andric auto *LiteralExpr = dyn_cast<ObjCStringLiteral>(Point->getStmt()); 10070b57cec5SDimitry Andric if (!LiteralExpr) 10080b57cec5SDimitry Andric return nullptr; 10090b57cec5SDimitry Andric 10100b57cec5SDimitry Andric SVal LiteralSVal = Succ->getSVal(LiteralExpr); 10110b57cec5SDimitry Andric if (LiteralSVal.getAsRegion() != NonLocalizedString) 10120b57cec5SDimitry Andric return nullptr; 10130b57cec5SDimitry Andric 10140b57cec5SDimitry Andric Satisfied = true; 10150b57cec5SDimitry Andric 10160b57cec5SDimitry Andric PathDiagnosticLocation L = 10170b57cec5SDimitry Andric PathDiagnosticLocation::create(*Point, BRC.getSourceManager()); 10180b57cec5SDimitry Andric 10190b57cec5SDimitry Andric if (!L.isValid() || !L.asLocation().isValid()) 10200b57cec5SDimitry Andric return nullptr; 10210b57cec5SDimitry Andric 10220b57cec5SDimitry Andric auto Piece = std::make_shared<PathDiagnosticEventPiece>( 10230b57cec5SDimitry Andric L, "Non-localized string literal here"); 10240b57cec5SDimitry Andric Piece->addRange(LiteralExpr->getSourceRange()); 10250b57cec5SDimitry Andric 10260b57cec5SDimitry Andric return std::move(Piece); 10270b57cec5SDimitry Andric } 10280b57cec5SDimitry Andric 10290b57cec5SDimitry Andric namespace { 10300b57cec5SDimitry Andric class EmptyLocalizationContextChecker 10310b57cec5SDimitry Andric : public Checker<check::ASTDecl<ObjCImplementationDecl>> { 10320b57cec5SDimitry Andric 10330b57cec5SDimitry Andric // A helper class, which walks the AST 10340b57cec5SDimitry Andric class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { 10350b57cec5SDimitry Andric const ObjCMethodDecl *MD; 10360b57cec5SDimitry Andric BugReporter &BR; 10370b57cec5SDimitry Andric AnalysisManager &Mgr; 10380b57cec5SDimitry Andric const CheckerBase *Checker; 10390b57cec5SDimitry Andric LocationOrAnalysisDeclContext DCtx; 10400b57cec5SDimitry Andric 10410b57cec5SDimitry Andric public: 10420b57cec5SDimitry Andric MethodCrawler(const ObjCMethodDecl *InMD, BugReporter &InBR, 10430b57cec5SDimitry Andric const CheckerBase *Checker, AnalysisManager &InMgr, 10440b57cec5SDimitry Andric AnalysisDeclContext *InDCtx) 10450b57cec5SDimitry Andric : MD(InMD), BR(InBR), Mgr(InMgr), Checker(Checker), DCtx(InDCtx) {} 10460b57cec5SDimitry Andric 10470b57cec5SDimitry Andric void VisitStmt(const Stmt *S) { VisitChildren(S); } 10480b57cec5SDimitry Andric 10490b57cec5SDimitry Andric void VisitObjCMessageExpr(const ObjCMessageExpr *ME); 10500b57cec5SDimitry Andric 10510b57cec5SDimitry Andric void reportEmptyContextError(const ObjCMessageExpr *M) const; 10520b57cec5SDimitry Andric 10530b57cec5SDimitry Andric void VisitChildren(const Stmt *S) { 10540b57cec5SDimitry Andric for (const Stmt *Child : S->children()) { 10550b57cec5SDimitry Andric if (Child) 10560b57cec5SDimitry Andric this->Visit(Child); 10570b57cec5SDimitry Andric } 10580b57cec5SDimitry Andric } 10590b57cec5SDimitry Andric }; 10600b57cec5SDimitry Andric 10610b57cec5SDimitry Andric public: 10620b57cec5SDimitry Andric void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, 10630b57cec5SDimitry Andric BugReporter &BR) const; 10640b57cec5SDimitry Andric }; 10650b57cec5SDimitry Andric } // end anonymous namespace 10660b57cec5SDimitry Andric 10670b57cec5SDimitry Andric void EmptyLocalizationContextChecker::checkASTDecl( 10680b57cec5SDimitry Andric const ObjCImplementationDecl *D, AnalysisManager &Mgr, 10690b57cec5SDimitry Andric BugReporter &BR) const { 10700b57cec5SDimitry Andric 10710b57cec5SDimitry Andric for (const ObjCMethodDecl *M : D->methods()) { 10720b57cec5SDimitry Andric AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M); 10730b57cec5SDimitry Andric 10740b57cec5SDimitry Andric const Stmt *Body = M->getBody(); 1075480093f4SDimitry Andric if (!Body) { 1076480093f4SDimitry Andric assert(M->isSynthesizedAccessorStub()); 1077480093f4SDimitry Andric continue; 1078480093f4SDimitry Andric } 10790b57cec5SDimitry Andric 10800b57cec5SDimitry Andric MethodCrawler MC(M->getCanonicalDecl(), BR, this, Mgr, DCtx); 10810b57cec5SDimitry Andric MC.VisitStmt(Body); 10820b57cec5SDimitry Andric } 10830b57cec5SDimitry Andric } 10840b57cec5SDimitry Andric 10850b57cec5SDimitry Andric /// This check attempts to match these macros, assuming they are defined as 10860b57cec5SDimitry Andric /// follows: 10870b57cec5SDimitry Andric /// 10880b57cec5SDimitry Andric /// #define NSLocalizedString(key, comment) \ 10890b57cec5SDimitry Andric /// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil] 10900b57cec5SDimitry Andric /// #define NSLocalizedStringFromTable(key, tbl, comment) \ 10910b57cec5SDimitry Andric /// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)] 10920b57cec5SDimitry Andric /// #define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \ 10930b57cec5SDimitry Andric /// [bundle localizedStringForKey:(key) value:@"" table:(tbl)] 10940b57cec5SDimitry Andric /// #define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) 10950b57cec5SDimitry Andric /// 10960b57cec5SDimitry Andric /// We cannot use the path sensitive check because the macro argument we are 10970b57cec5SDimitry Andric /// checking for (comment) is not used and thus not present in the AST, 10980b57cec5SDimitry Andric /// so we use Lexer on the original macro call and retrieve the value of 10990b57cec5SDimitry Andric /// the comment. If it's empty or nil, we raise a warning. 11000b57cec5SDimitry Andric void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr( 11010b57cec5SDimitry Andric const ObjCMessageExpr *ME) { 11020b57cec5SDimitry Andric 11030b57cec5SDimitry Andric // FIXME: We may be able to use PPCallbacks to check for empty context 11040b57cec5SDimitry Andric // comments as part of preprocessing and avoid this re-lexing hack. 11050b57cec5SDimitry Andric const ObjCInterfaceDecl *OD = ME->getReceiverInterface(); 11060b57cec5SDimitry Andric if (!OD) 11070b57cec5SDimitry Andric return; 11080b57cec5SDimitry Andric 11090b57cec5SDimitry Andric const IdentifierInfo *odInfo = OD->getIdentifier(); 11100b57cec5SDimitry Andric 11110b57cec5SDimitry Andric if (!(odInfo->isStr("NSBundle") && 11120b57cec5SDimitry Andric ME->getSelector().getAsString() == 11130b57cec5SDimitry Andric "localizedStringForKey:value:table:")) { 11140b57cec5SDimitry Andric return; 11150b57cec5SDimitry Andric } 11160b57cec5SDimitry Andric 11170b57cec5SDimitry Andric SourceRange R = ME->getSourceRange(); 11180b57cec5SDimitry Andric if (!R.getBegin().isMacroID()) 11190b57cec5SDimitry Andric return; 11200b57cec5SDimitry Andric 11210b57cec5SDimitry Andric // getImmediateMacroCallerLoc gets the location of the immediate macro 11220b57cec5SDimitry Andric // caller, one level up the stack toward the initial macro typed into the 11230b57cec5SDimitry Andric // source, so SL should point to the NSLocalizedString macro. 11240b57cec5SDimitry Andric SourceLocation SL = 11250b57cec5SDimitry Andric Mgr.getSourceManager().getImmediateMacroCallerLoc(R.getBegin()); 11260b57cec5SDimitry Andric std::pair<FileID, unsigned> SLInfo = 11270b57cec5SDimitry Andric Mgr.getSourceManager().getDecomposedLoc(SL); 11280b57cec5SDimitry Andric 11290b57cec5SDimitry Andric SrcMgr::SLocEntry SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first); 11300b57cec5SDimitry Andric 11310b57cec5SDimitry Andric // If NSLocalizedString macro is wrapped in another macro, we need to 11320b57cec5SDimitry Andric // unwrap the expansion until we get to the NSLocalizedStringMacro. 11330b57cec5SDimitry Andric while (SE.isExpansion()) { 11340b57cec5SDimitry Andric SL = SE.getExpansion().getSpellingLoc(); 11350b57cec5SDimitry Andric SLInfo = Mgr.getSourceManager().getDecomposedLoc(SL); 11360b57cec5SDimitry Andric SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first); 11370b57cec5SDimitry Andric } 11380b57cec5SDimitry Andric 1139bdd1243dSDimitry Andric std::optional<llvm::MemoryBufferRef> BF = 1140e8d8bef9SDimitry Andric Mgr.getSourceManager().getBufferOrNone(SLInfo.first, SL); 1141e8d8bef9SDimitry Andric if (!BF) 11420b57cec5SDimitry Andric return; 114381ad6265SDimitry Andric LangOptions LangOpts; 114481ad6265SDimitry Andric Lexer TheLexer(SL, LangOpts, BF->getBufferStart(), 11450b57cec5SDimitry Andric BF->getBufferStart() + SLInfo.second, BF->getBufferEnd()); 11460b57cec5SDimitry Andric 11470b57cec5SDimitry Andric Token I; 11480b57cec5SDimitry Andric Token Result; // This will hold the token just before the last ')' 11490b57cec5SDimitry Andric int p_count = 0; // This is for parenthesis matching 11500b57cec5SDimitry Andric while (!TheLexer.LexFromRawLexer(I)) { 11510b57cec5SDimitry Andric if (I.getKind() == tok::l_paren) 11520b57cec5SDimitry Andric ++p_count; 11530b57cec5SDimitry Andric if (I.getKind() == tok::r_paren) { 11540b57cec5SDimitry Andric if (p_count == 1) 11550b57cec5SDimitry Andric break; 11560b57cec5SDimitry Andric --p_count; 11570b57cec5SDimitry Andric } 11580b57cec5SDimitry Andric Result = I; 11590b57cec5SDimitry Andric } 11600b57cec5SDimitry Andric 11610b57cec5SDimitry Andric if (isAnyIdentifier(Result.getKind())) { 1162*0fca6ea1SDimitry Andric if (Result.getRawIdentifier() == "nil") { 11630b57cec5SDimitry Andric reportEmptyContextError(ME); 11640b57cec5SDimitry Andric return; 11650b57cec5SDimitry Andric } 11660b57cec5SDimitry Andric } 11670b57cec5SDimitry Andric 11680b57cec5SDimitry Andric if (!isStringLiteral(Result.getKind())) 11690b57cec5SDimitry Andric return; 11700b57cec5SDimitry Andric 11710b57cec5SDimitry Andric StringRef Comment = 11720b57cec5SDimitry Andric StringRef(Result.getLiteralData(), Result.getLength()).trim('"'); 11730b57cec5SDimitry Andric 11740b57cec5SDimitry Andric if ((Comment.trim().size() == 0 && Comment.size() > 0) || // Is Whitespace 11750b57cec5SDimitry Andric Comment.empty()) { 11760b57cec5SDimitry Andric reportEmptyContextError(ME); 11770b57cec5SDimitry Andric } 11780b57cec5SDimitry Andric } 11790b57cec5SDimitry Andric 11800b57cec5SDimitry Andric void EmptyLocalizationContextChecker::MethodCrawler::reportEmptyContextError( 11810b57cec5SDimitry Andric const ObjCMessageExpr *ME) const { 11820b57cec5SDimitry Andric // Generate the bug report. 11830b57cec5SDimitry Andric BR.EmitBasicReport(MD, Checker, "Context Missing", 11840b57cec5SDimitry Andric "Localizability Issue (Apple)", 11850b57cec5SDimitry Andric "Localized string macro should include a non-empty " 11860b57cec5SDimitry Andric "comment for translators", 11870b57cec5SDimitry Andric PathDiagnosticLocation(ME, BR.getSourceManager(), DCtx)); 11880b57cec5SDimitry Andric } 11890b57cec5SDimitry Andric 11900b57cec5SDimitry Andric namespace { 11910b57cec5SDimitry Andric class PluralMisuseChecker : public Checker<check::ASTCodeBody> { 11920b57cec5SDimitry Andric 11930b57cec5SDimitry Andric // A helper class, which walks the AST 11940b57cec5SDimitry Andric class MethodCrawler : public RecursiveASTVisitor<MethodCrawler> { 11950b57cec5SDimitry Andric BugReporter &BR; 11960b57cec5SDimitry Andric const CheckerBase *Checker; 11970b57cec5SDimitry Andric AnalysisDeclContext *AC; 11980b57cec5SDimitry Andric 11990b57cec5SDimitry Andric // This functions like a stack. We push on any IfStmt or 12000b57cec5SDimitry Andric // ConditionalOperator that matches the condition 12010b57cec5SDimitry Andric // and pop it off when we leave that statement 12020b57cec5SDimitry Andric llvm::SmallVector<const clang::Stmt *, 8> MatchingStatements; 12030b57cec5SDimitry Andric // This is true when we are the direct-child of a 12040b57cec5SDimitry Andric // matching statement 12050b57cec5SDimitry Andric bool InMatchingStatement = false; 12060b57cec5SDimitry Andric 12070b57cec5SDimitry Andric public: 12080b57cec5SDimitry Andric explicit MethodCrawler(BugReporter &InBR, const CheckerBase *Checker, 12090b57cec5SDimitry Andric AnalysisDeclContext *InAC) 12100b57cec5SDimitry Andric : BR(InBR), Checker(Checker), AC(InAC) {} 12110b57cec5SDimitry Andric 12120b57cec5SDimitry Andric bool VisitIfStmt(const IfStmt *I); 12130b57cec5SDimitry Andric bool EndVisitIfStmt(IfStmt *I); 12140b57cec5SDimitry Andric bool TraverseIfStmt(IfStmt *x); 12150b57cec5SDimitry Andric bool VisitConditionalOperator(const ConditionalOperator *C); 12160b57cec5SDimitry Andric bool TraverseConditionalOperator(ConditionalOperator *C); 12170b57cec5SDimitry Andric bool VisitCallExpr(const CallExpr *CE); 12180b57cec5SDimitry Andric bool VisitObjCMessageExpr(const ObjCMessageExpr *ME); 12190b57cec5SDimitry Andric 12200b57cec5SDimitry Andric private: 12210b57cec5SDimitry Andric void reportPluralMisuseError(const Stmt *S) const; 12220b57cec5SDimitry Andric bool isCheckingPlurality(const Expr *E) const; 12230b57cec5SDimitry Andric }; 12240b57cec5SDimitry Andric 12250b57cec5SDimitry Andric public: 12260b57cec5SDimitry Andric void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, 12270b57cec5SDimitry Andric BugReporter &BR) const { 12280b57cec5SDimitry Andric MethodCrawler Visitor(BR, this, Mgr.getAnalysisDeclContext(D)); 12290b57cec5SDimitry Andric Visitor.TraverseDecl(const_cast<Decl *>(D)); 12300b57cec5SDimitry Andric } 12310b57cec5SDimitry Andric }; 12320b57cec5SDimitry Andric } // end anonymous namespace 12330b57cec5SDimitry Andric 12340b57cec5SDimitry Andric // Checks the condition of the IfStmt and returns true if one 12350b57cec5SDimitry Andric // of the following heuristics are met: 12360b57cec5SDimitry Andric // 1) The conidtion is a variable with "singular" or "plural" in the name 12370b57cec5SDimitry Andric // 2) The condition is a binary operator with 1 or 2 on the right-hand side 12380b57cec5SDimitry Andric bool PluralMisuseChecker::MethodCrawler::isCheckingPlurality( 12390b57cec5SDimitry Andric const Expr *Condition) const { 12400b57cec5SDimitry Andric const BinaryOperator *BO = nullptr; 12410b57cec5SDimitry Andric // Accounts for when a VarDecl represents a BinaryOperator 12420b57cec5SDimitry Andric if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Condition)) { 12430b57cec5SDimitry Andric if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 12440b57cec5SDimitry Andric const Expr *InitExpr = VD->getInit(); 12450b57cec5SDimitry Andric if (InitExpr) { 12460b57cec5SDimitry Andric if (const BinaryOperator *B = 12470b57cec5SDimitry Andric dyn_cast<BinaryOperator>(InitExpr->IgnoreParenImpCasts())) { 12480b57cec5SDimitry Andric BO = B; 12490b57cec5SDimitry Andric } 12500b57cec5SDimitry Andric } 12517a6dacacSDimitry Andric if (VD->getName().contains_insensitive("plural") || 12527a6dacacSDimitry Andric VD->getName().contains_insensitive("singular")) { 12530b57cec5SDimitry Andric return true; 12540b57cec5SDimitry Andric } 12550b57cec5SDimitry Andric } 12560b57cec5SDimitry Andric } else if (const BinaryOperator *B = dyn_cast<BinaryOperator>(Condition)) { 12570b57cec5SDimitry Andric BO = B; 12580b57cec5SDimitry Andric } 12590b57cec5SDimitry Andric 12600b57cec5SDimitry Andric if (BO == nullptr) 12610b57cec5SDimitry Andric return false; 12620b57cec5SDimitry Andric 12630b57cec5SDimitry Andric if (IntegerLiteral *IL = dyn_cast_or_null<IntegerLiteral>( 12640b57cec5SDimitry Andric BO->getRHS()->IgnoreParenImpCasts())) { 12650b57cec5SDimitry Andric llvm::APInt Value = IL->getValue(); 12660b57cec5SDimitry Andric if (Value == 1 || Value == 2) { 12670b57cec5SDimitry Andric return true; 12680b57cec5SDimitry Andric } 12690b57cec5SDimitry Andric } 12700b57cec5SDimitry Andric return false; 12710b57cec5SDimitry Andric } 12720b57cec5SDimitry Andric 12730b57cec5SDimitry Andric // A CallExpr with "LOC" in its identifier that takes in a string literal 12740b57cec5SDimitry Andric // has been shown to almost always be a function that returns a localized 12750b57cec5SDimitry Andric // string. Raise a diagnostic when this is in a statement that matches 12760b57cec5SDimitry Andric // the condition. 12770b57cec5SDimitry Andric bool PluralMisuseChecker::MethodCrawler::VisitCallExpr(const CallExpr *CE) { 12780b57cec5SDimitry Andric if (InMatchingStatement) { 12790b57cec5SDimitry Andric if (const FunctionDecl *FD = CE->getDirectCallee()) { 12800b57cec5SDimitry Andric std::string NormalizedName = 12810b57cec5SDimitry Andric StringRef(FD->getNameInfo().getAsString()).lower(); 12820b57cec5SDimitry Andric if (NormalizedName.find("loc") != std::string::npos) { 12830b57cec5SDimitry Andric for (const Expr *Arg : CE->arguments()) { 12840b57cec5SDimitry Andric if (isa<ObjCStringLiteral>(Arg)) 12850b57cec5SDimitry Andric reportPluralMisuseError(CE); 12860b57cec5SDimitry Andric } 12870b57cec5SDimitry Andric } 12880b57cec5SDimitry Andric } 12890b57cec5SDimitry Andric } 12900b57cec5SDimitry Andric return true; 12910b57cec5SDimitry Andric } 12920b57cec5SDimitry Andric 12930b57cec5SDimitry Andric // The other case is for NSLocalizedString which also returns 12940b57cec5SDimitry Andric // a localized string. It's a macro for the ObjCMessageExpr 12950b57cec5SDimitry Andric // [NSBundle localizedStringForKey:value:table:] Raise a 12960b57cec5SDimitry Andric // diagnostic when this is in a statement that matches 12970b57cec5SDimitry Andric // the condition. 12980b57cec5SDimitry Andric bool PluralMisuseChecker::MethodCrawler::VisitObjCMessageExpr( 12990b57cec5SDimitry Andric const ObjCMessageExpr *ME) { 13000b57cec5SDimitry Andric const ObjCInterfaceDecl *OD = ME->getReceiverInterface(); 13010b57cec5SDimitry Andric if (!OD) 13020b57cec5SDimitry Andric return true; 13030b57cec5SDimitry Andric 13040b57cec5SDimitry Andric const IdentifierInfo *odInfo = OD->getIdentifier(); 13050b57cec5SDimitry Andric 13060b57cec5SDimitry Andric if (odInfo->isStr("NSBundle") && 13070b57cec5SDimitry Andric ME->getSelector().getAsString() == "localizedStringForKey:value:table:") { 13080b57cec5SDimitry Andric if (InMatchingStatement) { 13090b57cec5SDimitry Andric reportPluralMisuseError(ME); 13100b57cec5SDimitry Andric } 13110b57cec5SDimitry Andric } 13120b57cec5SDimitry Andric return true; 13130b57cec5SDimitry Andric } 13140b57cec5SDimitry Andric 13150b57cec5SDimitry Andric /// Override TraverseIfStmt so we know when we are done traversing an IfStmt 13160b57cec5SDimitry Andric bool PluralMisuseChecker::MethodCrawler::TraverseIfStmt(IfStmt *I) { 13170b57cec5SDimitry Andric RecursiveASTVisitor<MethodCrawler>::TraverseIfStmt(I); 13180b57cec5SDimitry Andric return EndVisitIfStmt(I); 13190b57cec5SDimitry Andric } 13200b57cec5SDimitry Andric 13210b57cec5SDimitry Andric // EndVisit callbacks are not provided by the RecursiveASTVisitor 13220b57cec5SDimitry Andric // so we override TraverseIfStmt and make a call to EndVisitIfStmt 13230b57cec5SDimitry Andric // after traversing the IfStmt 13240b57cec5SDimitry Andric bool PluralMisuseChecker::MethodCrawler::EndVisitIfStmt(IfStmt *I) { 13250b57cec5SDimitry Andric MatchingStatements.pop_back(); 13260b57cec5SDimitry Andric if (!MatchingStatements.empty()) { 13270b57cec5SDimitry Andric if (MatchingStatements.back() != nullptr) { 13280b57cec5SDimitry Andric InMatchingStatement = true; 13290b57cec5SDimitry Andric return true; 13300b57cec5SDimitry Andric } 13310b57cec5SDimitry Andric } 13320b57cec5SDimitry Andric InMatchingStatement = false; 13330b57cec5SDimitry Andric return true; 13340b57cec5SDimitry Andric } 13350b57cec5SDimitry Andric 13360b57cec5SDimitry Andric bool PluralMisuseChecker::MethodCrawler::VisitIfStmt(const IfStmt *I) { 1337349cc55cSDimitry Andric const Expr *Condition = I->getCond(); 1338349cc55cSDimitry Andric if (!Condition) 1339349cc55cSDimitry Andric return true; 1340349cc55cSDimitry Andric Condition = Condition->IgnoreParenImpCasts(); 13410b57cec5SDimitry Andric if (isCheckingPlurality(Condition)) { 13420b57cec5SDimitry Andric MatchingStatements.push_back(I); 13430b57cec5SDimitry Andric InMatchingStatement = true; 13440b57cec5SDimitry Andric } else { 13450b57cec5SDimitry Andric MatchingStatements.push_back(nullptr); 13460b57cec5SDimitry Andric InMatchingStatement = false; 13470b57cec5SDimitry Andric } 13480b57cec5SDimitry Andric 13490b57cec5SDimitry Andric return true; 13500b57cec5SDimitry Andric } 13510b57cec5SDimitry Andric 13520b57cec5SDimitry Andric // Preliminary support for conditional operators. 13530b57cec5SDimitry Andric bool PluralMisuseChecker::MethodCrawler::TraverseConditionalOperator( 13540b57cec5SDimitry Andric ConditionalOperator *C) { 13550b57cec5SDimitry Andric RecursiveASTVisitor<MethodCrawler>::TraverseConditionalOperator(C); 13560b57cec5SDimitry Andric MatchingStatements.pop_back(); 13570b57cec5SDimitry Andric if (!MatchingStatements.empty()) { 13580b57cec5SDimitry Andric if (MatchingStatements.back() != nullptr) 13590b57cec5SDimitry Andric InMatchingStatement = true; 13600b57cec5SDimitry Andric else 13610b57cec5SDimitry Andric InMatchingStatement = false; 13620b57cec5SDimitry Andric } else { 13630b57cec5SDimitry Andric InMatchingStatement = false; 13640b57cec5SDimitry Andric } 13650b57cec5SDimitry Andric return true; 13660b57cec5SDimitry Andric } 13670b57cec5SDimitry Andric 13680b57cec5SDimitry Andric bool PluralMisuseChecker::MethodCrawler::VisitConditionalOperator( 13690b57cec5SDimitry Andric const ConditionalOperator *C) { 13700b57cec5SDimitry Andric const Expr *Condition = C->getCond()->IgnoreParenImpCasts(); 13710b57cec5SDimitry Andric if (isCheckingPlurality(Condition)) { 13720b57cec5SDimitry Andric MatchingStatements.push_back(C); 13730b57cec5SDimitry Andric InMatchingStatement = true; 13740b57cec5SDimitry Andric } else { 13750b57cec5SDimitry Andric MatchingStatements.push_back(nullptr); 13760b57cec5SDimitry Andric InMatchingStatement = false; 13770b57cec5SDimitry Andric } 13780b57cec5SDimitry Andric return true; 13790b57cec5SDimitry Andric } 13800b57cec5SDimitry Andric 13810b57cec5SDimitry Andric void PluralMisuseChecker::MethodCrawler::reportPluralMisuseError( 13820b57cec5SDimitry Andric const Stmt *S) const { 13830b57cec5SDimitry Andric // Generate the bug report. 13840b57cec5SDimitry Andric BR.EmitBasicReport(AC->getDecl(), Checker, "Plural Misuse", 13850b57cec5SDimitry Andric "Localizability Issue (Apple)", 13860b57cec5SDimitry Andric "Plural cases are not supported across all languages. " 13870b57cec5SDimitry Andric "Use a .stringsdict file instead", 13880b57cec5SDimitry Andric PathDiagnosticLocation(S, BR.getSourceManager(), AC)); 13890b57cec5SDimitry Andric } 13900b57cec5SDimitry Andric 13910b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 13920b57cec5SDimitry Andric // Checker registration. 13930b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 13940b57cec5SDimitry Andric 13950b57cec5SDimitry Andric void ento::registerNonLocalizedStringChecker(CheckerManager &mgr) { 13960b57cec5SDimitry Andric NonLocalizedStringChecker *checker = 13970b57cec5SDimitry Andric mgr.registerChecker<NonLocalizedStringChecker>(); 13980b57cec5SDimitry Andric checker->IsAggressive = 13990b57cec5SDimitry Andric mgr.getAnalyzerOptions().getCheckerBooleanOption( 14000b57cec5SDimitry Andric checker, "AggressiveReport"); 14010b57cec5SDimitry Andric } 14020b57cec5SDimitry Andric 14035ffd83dbSDimitry Andric bool ento::shouldRegisterNonLocalizedStringChecker(const CheckerManager &mgr) { 14040b57cec5SDimitry Andric return true; 14050b57cec5SDimitry Andric } 14060b57cec5SDimitry Andric 14070b57cec5SDimitry Andric void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) { 14080b57cec5SDimitry Andric mgr.registerChecker<EmptyLocalizationContextChecker>(); 14090b57cec5SDimitry Andric } 14100b57cec5SDimitry Andric 14110b57cec5SDimitry Andric bool ento::shouldRegisterEmptyLocalizationContextChecker( 14125ffd83dbSDimitry Andric const CheckerManager &mgr) { 14130b57cec5SDimitry Andric return true; 14140b57cec5SDimitry Andric } 14150b57cec5SDimitry Andric 14160b57cec5SDimitry Andric void ento::registerPluralMisuseChecker(CheckerManager &mgr) { 14170b57cec5SDimitry Andric mgr.registerChecker<PluralMisuseChecker>(); 14180b57cec5SDimitry Andric } 14190b57cec5SDimitry Andric 14205ffd83dbSDimitry Andric bool ento::shouldRegisterPluralMisuseChecker(const CheckerManager &mgr) { 14210b57cec5SDimitry Andric return true; 14220b57cec5SDimitry Andric } 1423