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