31#include "llvm/ADT/STLExtras.h"
32#include "llvm/Support/Unicode.h"
39struct LocalizedState {
41 enum Kind { NonLocalized, Localized } K;
42 LocalizedState(Kind InK) : K(InK) {}
45 bool isLocalized()
const {
return K == Localized; }
46 bool isNonLocalized()
const {
return K == NonLocalized; }
48 static LocalizedState getLocalized() {
return LocalizedState(Localized); }
49 static LocalizedState getNonLocalized() {
50 return LocalizedState(NonLocalized);
54 bool operator==(
const LocalizedState &
X)
const {
return K ==
X.K; }
57 void Profile(llvm::FoldingSetNodeID &ID)
const {
ID.AddInteger(K); }
60class NonLocalizedStringChecker
61 :
public Checker<check::PreCall, check::PostCall, check::PreObjCMessage,
62 check::PostObjCMessage,
63 check::PostStmt<ObjCStringLiteral>> {
65 const BugType BT{
this,
"Unlocalizable string",
66 "Localizability Issue (Apple)"};
69 mutable llvm::DenseMap<
const IdentifierInfo *,
70 llvm::DenseMap<Selector, uint8_t>> UIMethods;
72 mutable llvm::SmallSet<std::pair<const IdentifierInfo *, Selector>, 12> LSM;
74 mutable llvm::SmallPtrSet<const IdentifierInfo *, 5> LSF;
76 void initUIMethods(ASTContext &Ctx)
const;
77 void initLocStringsMethods(ASTContext &Ctx)
const;
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;
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;
89 int getLocalizedArgumentForSelector(
const IdentifierInfo *Receiver,
96 bool IsAggressive =
false;
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;
117 NonLocalizedStringBRVisitor(
const MemRegion *NonLocalizedString)
118 : NonLocalizedString(NonLocalizedString), Satisfied(
false) {
119 assert(NonLocalizedString);
126 void Profile(llvm::FoldingSetNodeID &ID)
const override {
127 ID.Add(NonLocalizedString);
132#define NEW_RECEIVER(receiver) \
133 llvm::DenseMap<Selector, uint8_t> &receiver##M = \
134 UIMethods[&Ctx.Idents.get(#receiver)];
135#define ADD_NULLARY_METHOD(receiver, method, argument) \
136 receiver##M.insert( \
137 {Ctx.Selectors.getNullarySelector(&Ctx.Idents.get(#method)), argument});
138#define ADD_UNARY_METHOD(receiver, method, argument) \
139 receiver##M.insert( \
140 {Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(#method)), argument});
141#define ADD_METHOD(receiver, method_list, count, argument) \
142 receiver##M.insert({Ctx.Selectors.getSelector(count, method_list), argument});
146void NonLocalizedStringChecker::initUIMethods(
ASTContext &Ctx)
const {
147 if (!UIMethods.empty())
158 ADD_METHOD(UITabBarItem, initWithTitleUITabBarItemTag, 3, 0)
162 ADD_METHOD(UITabBarItem, initWithTitleUITabBarItemImage, 3, 0)
172 const IdentifierInfo *rowActionWithStyleUITableViewRowAction[] = {
175 ADD_METHOD(UITableViewRowAction, rowActionWithStyleUITableViewRowAction, 3, 1)
187 ADD_METHOD(NSButton, radioButtonWithTitleNSButton, 3, 0)
191 ADD_METHOD(NSButton, buttonWithTitleNSButtonImage, 4, 0)
195 ADD_METHOD(NSButton, checkboxWithTitleNSButton, 3, 0)
199 ADD_METHOD(NSButton, buttonWithTitleNSButtonTarget, 3, 0)
218 ADD_METHOD(NSBrowser, setTitleNSBrowser, 2, 0)
229 ADD_METHOD(UIAlertAction, actionWithTitleUIAlertAction, 3, 0)
235 ADD_METHOD(NSPopUpButton, insertItemWithTitleNSPopUpButton, 2, 0)
241 const IdentifierInfo *rowActionWithStyleNSTableViewRowAction[] = {
244 ADD_METHOD(NSTableViewRowAction, rowActionWithStyleNSTableViewRowAction, 3, 1)
276 ADD_METHOD(NSSegmentedControl, setLabelNSSegmentedControl, 2, 0)
279 ADD_METHOD(NSSegmentedControl, setToolTipNSSegmentedControl, 2, 0)
304 ADD_METHOD(NSMatrix, setToolTipNSMatrix, 2, 0)
320 ADD_METHOD(UIMenuItem, initWithTitleUIMenuItem, 2, 0)
324 const IdentifierInfo *alertControllerWithTitleUIAlertController[] = {
327 ADD_METHOD(UIAlertController, alertControllerWithTitleUIAlertController, 3, 1)
332 const IdentifierInfo *initWithTypeUIApplicationShortcutItemIcon[] = {
337 initWithTypeUIApplicationShortcutItemIcon, 5, 1)
340 ADD_METHOD(UIApplicationShortcutItem, initWithTypeUIApplicationShortcutItem,
347 &Ctx.
Idents.
get(
"destructiveButtonTitle"),
349 ADD_METHOD(UIActionSheet, initWithTitleUIActionSheet, 5, 0)
354 const IdentifierInfo *initWithNameUIAccessibilityCustomAction[] = {
358 initWithNameUIAccessibilityCustomAction, 3, 0)
385 ADD_METHOD(NSAttributedString, initWithStringNSAttributedString, 2, 0)
394 ADD_METHOD(UIKeyCommand, keyCommandWithInputUIKeyCommand, 4, 3)
404 &Ctx.
Idents.
get(
"informativeTextWithFormat")};
405 ADD_METHOD(NSAlert, alertWithMessageTextNSAlert, 5, 0)
424 ADD_METHOD(NSWindow, minFrameWidthWithTitleNSWindow, 2, 0)
431 const IdentifierInfo *addOptionWithTitleUIDocumentMenuViewController[] = {
435 addOptionWithTitleUIDocumentMenuViewController, 4, 0)
447 ADD_METHOD(UIAlertView, initWithTitleUIAlertView, 5, 0)
477 ADD_METHOD(NSSegmentedCell, setLabelNSSegmentedCell, 2, 0)
480 ADD_METHOD(NSSegmentedCell, setToolTipNSSegmentedCell, 2, 0)
491 ADD_METHOD(NSMenuItem, initWithTitleNSMenuItem, 3, 0)
498 ADD_METHOD(NSPopUpButtonCell, initTextCellNSPopUpButtonCell, 2, 0)
502 ADD_METHOD(NSPopUpButtonCell, insertItemWithTitleNSPopUpButtonCell, 2, 0)
515 ADD_METHOD(NSMenu, insertItemWithTitleNSMenu, 4, 0)
519 ADD_METHOD(NSMenu, addItemWithTitleNSMenu, 3, 0)
535 const IdentifierInfo *actionWithIdentifierNSUserNotificationAction[] = {
538 actionWithIdentifierNSUserNotificationAction, 2, 1)
548 ADD_METHOD(UIBarButtonItem, initWithTitleUIBarButtonItem, 4, 0)
554 const IdentifierInfo *insertSegmentWithTitleUISegmentedControl[] = {
557 ADD_METHOD(UISegmentedControl, insertSegmentWithTitleUISegmentedControl, 3, 0)
560 ADD_METHOD(UISegmentedControl, setTitleUISegmentedControl, 2, 0)
564 *initWithItemLoadingTokenNSAccessibilityCustomRotorItemResult[] = {
565 &Ctx.
Idents.
get(
"initWithItemLoadingToken"),
567 ADD_METHOD(NSAccessibilityCustomRotorItemResult,
568 initWithItemLoadingTokenNSAccessibilityCustomRotorItemResult, 2, 1)
572 const IdentifierInfo *contextualActionWithStyleUIContextualAction[] = {
575 ADD_METHOD(UIContextualAction, contextualActionWithStyleUIContextualAction, 3,
580 const IdentifierInfo *initWithLabelNSAccessibilityCustomRotor[] = {
583 initWithLabelNSAccessibilityCustomRotor, 2, 0)
591 const IdentifierInfo *initWithNameNSAccessibilityCustomAction[] = {
594 initWithNameNSAccessibilityCustomAction, 2, 0)
595 const IdentifierInfo *initWithNameTargetNSAccessibilityCustomAction[] = {
599 initWithNameTargetNSAccessibilityCustomAction, 3, 0)
603#define LSF_INSERT(function_name) LSF.insert(&Ctx.Idents.get(function_name));
604#define LSM_INSERT_NULLARY(receiver, method_name) \
605 LSM.insert({&Ctx.Idents.get(receiver), Ctx.Selectors.getNullarySelector( \
606 &Ctx.Idents.get(method_name))});
607#define LSM_INSERT_UNARY(receiver, method_name) \
608 LSM.insert({&Ctx.Idents.get(receiver), \
609 Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(method_name))});
610#define LSM_INSERT_SELECTOR(receiver, method_list, arguments) \
611 LSM.insert({&Ctx.Idents.get(receiver), \
612 Ctx.Selectors.getSelector(arguments, method_list)});
615void NonLocalizedStringChecker::initLocStringsMethods(
ASTContext &Ctx)
const {
633 LSF_INSERT(
"CFDateFormatterCreateStringWithDate");
634 LSF_INSERT(
"CFDateFormatterCreateStringWithAbsoluteTime");
635 LSF_INSERT(
"CFNumberFormatterCreateStringWithNumber");
640bool NonLocalizedStringChecker::isAnnotatedAsReturningLocalized(
641 const Decl *D)
const {
647 return Ann->getAnnotation() ==
"returns_localized_nsstring";
653bool NonLocalizedStringChecker::isAnnotatedAsTakingLocalized(
654 const Decl *D)
const {
660 return Ann->getAnnotation() ==
"takes_localized_nsstring";
665bool NonLocalizedStringChecker::hasLocalizedState(SVal S,
666 CheckerContext &
C)
const {
669 const LocalizedState *LS =
C.getState()->get<LocalizedMemMap>(mt);
670 if (LS && LS->isLocalized())
678bool NonLocalizedStringChecker::hasNonLocalizedState(SVal S,
679 CheckerContext &
C)
const {
682 const LocalizedState *LS =
C.getState()->get<LocalizedMemMap>(mt);
683 if (LS && LS->isNonLocalized())
690void NonLocalizedStringChecker::setLocalizedState(
const SVal S,
691 CheckerContext &
C)
const {
695 C.getState()->set<LocalizedMemMap>(mt, LocalizedState::getLocalized());
696 C.addTransition(State);
701void NonLocalizedStringChecker::setNonLocalizedState(
const SVal S,
702 CheckerContext &
C)
const {
706 mt, LocalizedState::getNonLocalized());
707 C.addTransition(State);
713 return StringRef(name).contains_insensitive(
"debug");
721 const Decl *D =
C.getCurrentAnalysisDeclContext()->getDecl();
725 if (
auto *ND = dyn_cast<NamedDecl>(D)) {
732 if (
auto *CD = dyn_cast<ObjCContainerDecl>(DC)) {
742void NonLocalizedStringChecker::reportLocalizationError(
743 SVal S,
const CallEvent &M, CheckerContext &
C,
int argumentNumber)
const {
750 ExplodedNode *ErrNode =
C.generateNonFatalErrorNode();
756 auto R = std::make_unique<PathSensitiveBugReport>(
757 BT,
"User-facing text should use localized string macro", ErrNode);
758 if (argumentNumber) {
763 R->markInteresting(S);
767 R->addVisitor(std::make_unique<NonLocalizedStringBRVisitor>(StringRegion));
769 C.emitReport(std::move(R));
774int NonLocalizedStringChecker::getLocalizedArgumentForSelector(
775 const IdentifierInfo *Receiver, Selector S)
const {
776 auto method = UIMethods.find(Receiver);
778 if (method == UIMethods.end())
781 auto argumentIterator = method->getSecond().find(S);
783 if (argumentIterator == method->getSecond().end())
786 int argumentNumber = argumentIterator->getSecond();
787 return argumentNumber;
791void NonLocalizedStringChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
792 CheckerContext &
C)
const {
793 initUIMethods(
C.getASTContext());
803 StringRef SelectorName = SelectorString;
804 assert(!SelectorName.empty());
806 if (odInfo->
isStr(
"NSString")) {
810 if (!(SelectorName.starts_with(
"drawAtPoint") ||
811 SelectorName.starts_with(
"drawInRect") ||
812 SelectorName.starts_with(
"drawWithRect")))
817 bool isNonLocalized = hasNonLocalizedState(svTitle,
C);
819 if (isNonLocalized) {
820 reportLocalizationError(svTitle, msg,
C);
824 int argumentNumber = getLocalizedArgumentForSelector(odInfo, S);
826 while (argumentNumber < 0 && OD->getSuperClass() !=
nullptr) {
828 argumentNumber = getLocalizedArgumentForSelector(P->getIdentifier(), S);
829 if (argumentNumber >= 0)
832 if (argumentNumber < 0) {
834 argumentNumber = getLocalizedArgumentForSelector(OD->
getIdentifier(), S);
838 if (argumentNumber < 0) {
839 if (
const Decl *D = msg.
getDecl()) {
840 if (
const ObjCMethodDecl *OMD = dyn_cast_or_null<ObjCMethodDecl>(D)) {
841 for (
auto [Idx, FormalParam] : llvm::enumerate(OMD->parameters())) {
842 if (isAnnotatedAsTakingLocalized(FormalParam)) {
843 argumentNumber = Idx;
851 if (argumentNumber < 0)
854 SVal svTitle = msg.
getArgSVal(argumentNumber);
856 if (
const ObjCStringRegion *SR =
857 dyn_cast_or_null<ObjCStringRegion>(svTitle.
getAsRegion())) {
858 StringRef stringValue =
859 SR->getObjCStringLiteral()->getString()->getString();
860 if ((stringValue.trim().size() == 0 && stringValue.size() > 0) ||
863 if (!IsAggressive && llvm::sys::unicode::columnWidthUTF8(stringValue) < 2)
867 bool isNonLocalized = hasNonLocalizedState(svTitle,
C);
869 if (isNonLocalized) {
870 reportLocalizationError(svTitle, msg,
C, argumentNumber + 1);
874void NonLocalizedStringChecker::checkPreCall(
const CallEvent &
Call,
875 CheckerContext &
C)
const {
876 const auto *FD = dyn_cast_or_null<FunctionDecl>(
Call.getDecl());
880 auto formals = FD->parameters();
881 for (
unsigned i = 0, ei = std::min(
static_cast<unsigned>(formals.size()),
882 Call.getNumArgs()); i != ei; ++i) {
883 if (isAnnotatedAsTakingLocalized(formals[i])) {
884 auto actual =
Call.getArgSVal(i);
885 if (hasNonLocalizedState(actual,
C)) {
886 reportLocalizationError(actual,
Call,
C, i + 1);
905 return ClsName == &Ctx.
Idents.
get(
"NSString") ||
906 ClsName == &Ctx.
Idents.
get(
"NSMutableString");
914void NonLocalizedStringChecker::checkPostCall(
const CallEvent &
Call,
915 CheckerContext &
C)
const {
916 initLocStringsMethods(
C.getASTContext());
918 if (!
Call.getOriginExpr())
924 const QualType RT =
Call.getResultType();
926 for (
unsigned i = 0; i <
Call.getNumArgs(); ++i) {
927 SVal argValue =
Call.getArgSVal(i);
928 if (hasLocalizedState(argValue,
C)) {
929 SVal sv =
Call.getReturnValue();
930 setLocalizedState(sv,
C);
940 const IdentifierInfo *Identifier =
Call.getCalleeIdentifier();
942 SVal sv =
Call.getReturnValue();
943 if (isAnnotatedAsReturningLocalized(D) || LSF.contains(Identifier)) {
944 setLocalizedState(sv,
C);
946 !hasLocalizedState(sv,
C)) {
948 setNonLocalizedState(sv,
C);
950 const SymbolicRegion *SymReg =
951 dyn_cast_or_null<SymbolicRegion>(sv.
getAsRegion());
953 setNonLocalizedState(sv,
C);
960void NonLocalizedStringChecker::checkPostObjCMessage(
const ObjCMethodCall &msg,
961 CheckerContext &
C)
const {
962 initLocStringsMethods(
C.getASTContext());
974 std::pair<const IdentifierInfo *, Selector> MethodDescription = {odInfo, S};
976 if (LSM.count(MethodDescription) ||
977 isAnnotatedAsReturningLocalized(msg.
getDecl())) {
979 setLocalizedState(sv,
C);
984void NonLocalizedStringChecker::checkPostStmt(
const ObjCStringLiteral *SL,
985 CheckerContext &
C)
const {
986 SVal sv =
C.getSVal(SL);
987 setNonLocalizedState(sv,
C);
991NonLocalizedStringBRVisitor::VisitNode(
const ExplodedNode *Succ,
992 BugReporterContext &BRC,
993 PathSensitiveBugReport &BR) {
1001 auto *LiteralExpr = dyn_cast<ObjCStringLiteral>(Point->getStmt());
1005 SVal LiteralSVal = Succ->
getSVal(LiteralExpr);
1006 if (LiteralSVal.
getAsRegion() != NonLocalizedString)
1011 PathDiagnosticLocation L =
1017 auto Piece = std::make_shared<PathDiagnosticEventPiece>(
1018 L,
"Non-localized string literal here");
1019 Piece->addRange(LiteralExpr->getSourceRange());
1021 return std::move(Piece);
1025class EmptyLocalizationContextChecker
1026 :
public Checker<check::ASTDecl<ObjCImplementationDecl>> {
1029 class MethodCrawler :
public ConstStmtVisitor<MethodCrawler> {
1030 const ObjCMethodDecl *MD;
1032 AnalysisManager &Mgr;
1033 const CheckerBase *Checker;
1037 MethodCrawler(
const ObjCMethodDecl *InMD, BugReporter &InBR,
1038 const CheckerBase *Checker, AnalysisManager &InMgr,
1039 AnalysisDeclContext *InDCtx)
1040 : MD(InMD), BR(InBR), Mgr(InMgr), Checker(Checker), DCtx(InDCtx) {}
1042 void VisitStmt(
const Stmt *S) { VisitChildren(S); }
1044 void VisitObjCMessageExpr(
const ObjCMessageExpr *ME);
1046 void reportEmptyContextError(
const ObjCMessageExpr *M)
const;
1048 void VisitChildren(
const Stmt *S) {
1049 for (
const Stmt *Child : S->
children()) {
1057 void checkASTDecl(
const ObjCImplementationDecl *D, AnalysisManager &Mgr,
1058 BugReporter &BR)
const;
1062void EmptyLocalizationContextChecker::checkASTDecl(
1063 const ObjCImplementationDecl *D, AnalysisManager &Mgr,
1064 BugReporter &BR)
const {
1066 for (
const ObjCMethodDecl *M : D->
methods()) {
1069 const Stmt *Body = M->getBody();
1071 assert(M->isSynthesizedAccessorStub());
1075 MethodCrawler MC(M->getCanonicalDecl(), BR,
this, Mgr, DCtx);
1095void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr(
1096 const ObjCMessageExpr *ME) {
1106 if (!(odInfo->
isStr(
"NSBundle") &&
1108 "localizedStringForKey:value:table:")) {
1133 std::optional<llvm::MemoryBufferRef> BF =
1137 LangOptions LangOpts;
1138 Lexer TheLexer(SL, LangOpts, BF->getBufferStart(),
1139 BF->getBufferStart() + SLInfo.second, BF->getBufferEnd());
1144 while (!TheLexer.LexFromRawLexer(I)) {
1145 if (I.
getKind() == tok::l_paren)
1147 if (I.
getKind() == tok::r_paren) {
1156 if (
Result.getRawIdentifier() ==
"nil") {
1157 reportEmptyContextError(ME);
1166 StringRef(
Result.getLiteralData(),
Result.getLength()).trim(
'"');
1168 if ((Comment.trim().size() == 0 && Comment.size() > 0) ||
1170 reportEmptyContextError(ME);
1174void EmptyLocalizationContextChecker::MethodCrawler::reportEmptyContextError(
1175 const ObjCMessageExpr *ME)
const {
1178 "Localizability Issue (Apple)",
1179 "Localized string macro should include a non-empty "
1180 "comment for translators",
1185class PluralMisuseChecker :
public Checker<check::ASTCodeBody> {
1190 const CheckerBase *Checker;
1191 AnalysisDeclContext *AC;
1196 llvm::SmallVector<const clang::Stmt *, 8> MatchingStatements;
1199 bool InMatchingStatement =
false;
1202 explicit MethodCrawler(BugReporter &InBR,
const CheckerBase *Checker,
1203 AnalysisDeclContext *InAC)
1204 : BR(InBR), Checker(Checker), AC(InAC) {}
1206 bool VisitIfStmt(IfStmt *I)
override;
1207 bool EndVisitIfStmt(IfStmt *I);
1208 bool TraverseIfStmt(IfStmt *x)
override;
1209 bool VisitConditionalOperator(ConditionalOperator *
C)
override;
1210 bool TraverseConditionalOperator(ConditionalOperator *
C)
override;
1211 bool VisitCallExpr(CallExpr *CE)
override;
1212 bool VisitObjCMessageExpr(ObjCMessageExpr *ME)
override;
1215 void reportPluralMisuseError(
const Stmt *S)
const;
1216 bool isCheckingPlurality(
const Expr *E)
const;
1220 void checkASTCodeBody(
const Decl *D, AnalysisManager &Mgr,
1221 BugReporter &BR)
const {
1223 Visitor.TraverseDecl(
const_cast<Decl *
>(D));
1232bool PluralMisuseChecker::MethodCrawler::isCheckingPlurality(
1234 const BinaryOperator *BO =
nullptr;
1236 if (
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(
Condition)) {
1237 if (
const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
1238 const Expr *InitExpr = VD->getInit();
1240 if (
const BinaryOperator *B =
1245 if (VD->getName().contains_insensitive(
"plural") ||
1246 VD->getName().contains_insensitive(
"singular")) {
1250 }
else if (
const BinaryOperator *B = dyn_cast<BinaryOperator>(
Condition)) {
1257 if (IntegerLiteral *IL = dyn_cast_or_null<IntegerLiteral>(
1259 llvm::APInt
Value = IL->getValue();
1271bool PluralMisuseChecker::MethodCrawler::VisitCallExpr(CallExpr *CE) {
1272 if (InMatchingStatement) {
1274 std::string NormalizedName =
1275 StringRef(FD->getNameInfo().getAsString()).lower();
1276 if (NormalizedName.find(
"loc") != std::string::npos) {
1277 for (
const Expr *Arg : CE->
arguments()) {
1279 reportPluralMisuseError(CE);
1292bool PluralMisuseChecker::MethodCrawler::VisitObjCMessageExpr(
1293 ObjCMessageExpr *ME) {
1300 if (odInfo->
isStr(
"NSBundle") &&
1302 if (InMatchingStatement) {
1303 reportPluralMisuseError(ME);
1310bool PluralMisuseChecker::MethodCrawler::TraverseIfStmt(IfStmt *I) {
1311 DynamicRecursiveASTVisitor::TraverseIfStmt(I);
1312 return EndVisitIfStmt(I);
1318bool PluralMisuseChecker::MethodCrawler::EndVisitIfStmt(IfStmt *I) {
1319 MatchingStatements.pop_back();
1320 if (!MatchingStatements.empty()) {
1321 if (MatchingStatements.back() !=
nullptr) {
1322 InMatchingStatement =
true;
1326 InMatchingStatement =
false;
1330bool PluralMisuseChecker::MethodCrawler::VisitIfStmt(IfStmt *I) {
1336 MatchingStatements.push_back(I);
1337 InMatchingStatement =
true;
1339 MatchingStatements.push_back(
nullptr);
1340 InMatchingStatement =
false;
1347bool PluralMisuseChecker::MethodCrawler::TraverseConditionalOperator(
1348 ConditionalOperator *
C) {
1349 DynamicRecursiveASTVisitor::TraverseConditionalOperator(
C);
1350 MatchingStatements.pop_back();
1351 if (!MatchingStatements.empty()) {
1352 if (MatchingStatements.back() !=
nullptr)
1353 InMatchingStatement =
true;
1355 InMatchingStatement =
false;
1357 InMatchingStatement =
false;
1362bool PluralMisuseChecker::MethodCrawler::VisitConditionalOperator(
1363 ConditionalOperator *
C) {
1364 const Expr *
Condition =
C->getCond()->IgnoreParenImpCasts();
1366 MatchingStatements.push_back(
C);
1367 InMatchingStatement =
true;
1369 MatchingStatements.push_back(
nullptr);
1370 InMatchingStatement =
false;
1375void PluralMisuseChecker::MethodCrawler::reportPluralMisuseError(
1376 const Stmt *S)
const {
1379 "Localizability Issue (Apple)",
1380 "Plural cases are not supported across all languages. "
1381 "Use a .stringsdict file instead",
1389void ento::registerNonLocalizedStringChecker(CheckerManager &mgr) {
1390 NonLocalizedStringChecker *checker =
1392 checker->IsAggressive =
1394 checker,
"AggressiveReport");
1397bool ento::shouldRegisterNonLocalizedStringChecker(
const CheckerManager &mgr) {
1401void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) {
1405bool ento::shouldRegisterEmptyLocalizationContextChecker(
1406 const CheckerManager &mgr) {
1410void ento::registerPluralMisuseChecker(CheckerManager &mgr) {
1414bool ento::shouldRegisterPluralMisuseChecker(
const CheckerManager &mgr) {
#define LSM_INSERT_SELECTOR(receiver, method_list, arguments)
#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)
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Decl - This represents one declaration (or definition), e.g.
DeclContext * getDeclContext()
specific_attr_iterator< T > specific_attr_end() const
specific_attr_iterator< T > specific_attr_begin() const
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
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.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
method_range methods() const
Represents an ObjC class declaration.
all_protocol_range all_referenced_protocols() const
ObjCInterfaceDecl * getSuperClass() const
Selector getSelector() const
ObjCInterfaceDecl * getReceiverInterface() const
Retrieve the Objective-C interface to which this message is being directed, if known.
Represents a pointer to an Objective C object.
const ObjCObjectType * getObjectType() const
Gets the type pointed to by this ObjC pointer.
std::optional< T > getAs() const
Convert to the specified ProgramPoint type, returning std::nullopt if this ProgramPoint is not of the...
A (possibly-)qualified type.
std::string getAsString() const
Derive the full selector name (e.g.
bool isValid() const
Return true if this is a valid SourceLocation object.
FileIDAndOffset getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
SourceLocation getImmediateMacroCallerLoc(SourceLocation Loc) const
Gets the location of the immediate macro caller, one level up the stack toward the initial macro type...
std::optional< llvm::MemoryBufferRef > getBufferOrNone(FileID FID, SourceLocation Loc=SourceLocation()) const
Return the buffer for the specified FileID.
const SrcMgr::SLocEntry & getSLocEntry(FileID FID, bool *Invalid=nullptr) const
SourceLocation getBegin() const
SourceLocation getSpellingLoc() const
const ExpansionInfo & getExpansion() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
tok::TokenKind getKind() const
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
SourceManager & getSourceManager() override
const SourceManager & getSourceManager() const
BugReporterVisitors are used to add custom diagnostics along a path.
virtual void Profile(llvm::FoldingSetNodeID &ID) const =0
virtual PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, PathSensitiveBugReport &BR)=0
Return a diagnostic piece which should be associated with the given node.
const SourceManager & getSourceManager()
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerFrontend *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges={}, ArrayRef< FixItHint > Fixits={})
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
virtual const Expr * getArgExpr(unsigned Index) const
Returns the expression associated with a given argument.
SVal getReturnValue() const
Returns the return value of the call.
virtual SourceRange getSourceRange() const
Returns a source range for the entire call, suitable for outputting in diagnostics.
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
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.
const ObjCMethodDecl * getDecl() const override
Returns the declaration of the function or method that will be called.
bool isInstanceMessage() const
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
const ObjCInterfaceDecl * getReceiverInterface() const
Get the interface for the receiver.
Selector getSelector() const
FullSourceLoc asLocation() const
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
const MemRegion * getAsRegion() const
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
llvm::PointerUnion< const LocationContext *, AnalysisDeclContext * > LocationOrAnalysisDeclContext
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
bool isStringLiteral(TokenKind K)
Return true if this is a C or C++ string-literal (or C++11 user-defined-string-literal) token.
bool isAnyIdentifier(TokenKind K)
Return true if this is a raw identifier or an identifier kind.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
std::pair< FileID, unsigned > FileIDAndOffset
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
@ Result
The result type of a method or function.
const FunctionProtoType * T
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor