llvm/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp

//=- LocalizationChecker.cpp -------------------------------------*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//  This file defines a set of checks for localizability including:
//  1) A checker that warns about uses of non-localized NSStrings passed to
//     UI methods expecting localized strings
//  2) A syntactic checker that warns against the bad practice of
//     not including a comment in NSLocalizedString macros.
//
//===----------------------------------------------------------------------===//

#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Lex/Lexer.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Unicode.h"
#include <optional>

usingnamespaceclang;
usingnamespaceento;

namespace {
struct LocalizedState {};

class NonLocalizedStringChecker
    : public Checker<check::PreCall, check::PostCall, check::PreObjCMessage,
                     check::PostObjCMessage,
                     check::PostStmt<ObjCStringLiteral>> {};

} // end anonymous namespace

REGISTER_MAP_WITH_PROGRAMSTATE()

namespace {
class NonLocalizedStringBRVisitor final : public BugReporterVisitor {};
} // End anonymous namespace.

#define NEW_RECEIVER(receiver)
#define ADD_NULLARY_METHOD(receiver, method, argument)
#define ADD_UNARY_METHOD(receiver, method, argument)
#define ADD_METHOD(receiver, method_list, count, argument)

/// Initializes a list of methods that require a localized string
/// Format: {"ClassName", {{"selectorName:", LocStringArg#}, ...}, ...}
void NonLocalizedStringChecker::initUIMethods(ASTContext &Ctx) const {}

#define LSF_INSERT(function_name)
#define LSM_INSERT_NULLARY(receiver, method_name)
#define LSM_INSERT_UNARY(receiver, method_name)
#define LSM_INSERT_SELECTOR(receiver, method_list, arguments)

/// Initializes a list of methods and C functions that return a localized string
void NonLocalizedStringChecker::initLocStringsMethods(ASTContext &Ctx) const {}

/// Checks to see if the method / function declaration includes
/// __attribute__((annotate("returns_localized_nsstring")))
bool NonLocalizedStringChecker::isAnnotatedAsReturningLocalized(
    const Decl *D) const {}

/// Checks to see if the method / function declaration includes
/// __attribute__((annotate("takes_localized_nsstring")))
bool NonLocalizedStringChecker::isAnnotatedAsTakingLocalized(
    const Decl *D) const {}

/// Returns true if the given SVal is marked as Localized in the program state
bool NonLocalizedStringChecker::hasLocalizedState(SVal S,
                                                  CheckerContext &C) const {}

/// Returns true if the given SVal is marked as NonLocalized in the program
/// state
bool NonLocalizedStringChecker::hasNonLocalizedState(SVal S,
                                                     CheckerContext &C) const {}

/// Marks the given SVal as Localized in the program state
void NonLocalizedStringChecker::setLocalizedState(const SVal S,
                                                  CheckerContext &C) const {}

/// Marks the given SVal as NonLocalized in the program state
void NonLocalizedStringChecker::setNonLocalizedState(const SVal S,
                                                     CheckerContext &C) const {}


static bool isDebuggingName(std::string name) {}

/// Returns true when, heuristically, the analyzer may be analyzing debugging
/// code. We use this to suppress localization diagnostics in un-localized user
/// interfaces that are only used for debugging and are therefore not user
/// facing.
static bool isDebuggingContext(CheckerContext &C) {}


/// Reports a localization error for the passed in method call and SVal
void NonLocalizedStringChecker::reportLocalizationError(
    SVal S, const CallEvent &M, CheckerContext &C, int argumentNumber) const {}

/// Returns the argument number requiring localized string if it exists
/// otherwise, returns -1
int NonLocalizedStringChecker::getLocalizedArgumentForSelector(
    const IdentifierInfo *Receiver, Selector S) const {}

/// Check if the string being passed in has NonLocalized state
void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
                                                    CheckerContext &C) const {}

void NonLocalizedStringChecker::checkPreCall(const CallEvent &Call,
                                             CheckerContext &C) const {}

static inline bool isNSStringType(QualType T, ASTContext &Ctx) {}

/// Marks a string being returned by any call as localized
/// if it is in LocStringFunctions (LSF) or the function is annotated.
/// Otherwise, we mark it as NonLocalized (Aggressive) or
/// NonLocalized only if it is not backed by a SymRegion (Non-Aggressive),
/// basically leaving only string literals as NonLocalized.
void NonLocalizedStringChecker::checkPostCall(const CallEvent &Call,
                                              CheckerContext &C) const {}

/// Marks a string being returned by an ObjC method as localized
/// if it is in LocStringMethods or the method is annotated
void NonLocalizedStringChecker::checkPostObjCMessage(const ObjCMethodCall &msg,
                                                     CheckerContext &C) const {}

/// Marks all empty string literals as localized
void NonLocalizedStringChecker::checkPostStmt(const ObjCStringLiteral *SL,
                                              CheckerContext &C) const {}

PathDiagnosticPieceRef
NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ,
                                       BugReporterContext &BRC,
                                       PathSensitiveBugReport &BR) {}

namespace {
class EmptyLocalizationContextChecker
    : public Checker<check::ASTDecl<ObjCImplementationDecl>> {};
} // end anonymous namespace

void EmptyLocalizationContextChecker::checkASTDecl(
    const ObjCImplementationDecl *D, AnalysisManager &Mgr,
    BugReporter &BR) const {}

/// This check attempts to match these macros, assuming they are defined as
/// follows:
///
/// #define NSLocalizedString(key, comment) \
/// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]
/// #define NSLocalizedStringFromTable(key, tbl, comment) \
/// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)]
/// #define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \
/// [bundle localizedStringForKey:(key) value:@"" table:(tbl)]
/// #define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment)
///
/// We cannot use the path sensitive check because the macro argument we are
/// checking for (comment) is not used and thus not present in the AST,
/// so we use Lexer on the original macro call and retrieve the value of
/// the comment. If it's empty or nil, we raise a warning.
void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr(
    const ObjCMessageExpr *ME) {}

void EmptyLocalizationContextChecker::MethodCrawler::reportEmptyContextError(
    const ObjCMessageExpr *ME) const {}

namespace {
class PluralMisuseChecker : public Checker<check::ASTCodeBody> {};
} // end anonymous namespace

// Checks the condition of the IfStmt and returns true if one
// of the following heuristics are met:
// 1) The conidtion is a variable with "singular" or "plural" in the name
// 2) The condition is a binary operator with 1 or 2 on the right-hand side
bool PluralMisuseChecker::MethodCrawler::isCheckingPlurality(
    const Expr *Condition) const {}

// A CallExpr with "LOC" in its identifier that takes in a string literal
// has been shown to almost always be a function that returns a localized
// string. Raise a diagnostic when this is in a statement that matches
// the condition.
bool PluralMisuseChecker::MethodCrawler::VisitCallExpr(const CallExpr *CE) {}

// The other case is for NSLocalizedString which also returns
// a localized string. It's a macro for the ObjCMessageExpr
// [NSBundle localizedStringForKey:value:table:] Raise a
// diagnostic when this is in a statement that matches
// the condition.
bool PluralMisuseChecker::MethodCrawler::VisitObjCMessageExpr(
    const ObjCMessageExpr *ME) {}

/// Override TraverseIfStmt so we know when we are done traversing an IfStmt
bool PluralMisuseChecker::MethodCrawler::TraverseIfStmt(IfStmt *I) {}

// EndVisit callbacks are not provided by the RecursiveASTVisitor
// so we override TraverseIfStmt and make a call to EndVisitIfStmt
// after traversing the IfStmt
bool PluralMisuseChecker::MethodCrawler::EndVisitIfStmt(IfStmt *I) {}

bool PluralMisuseChecker::MethodCrawler::VisitIfStmt(const IfStmt *I) {}

// Preliminary support for conditional operators.
bool PluralMisuseChecker::MethodCrawler::TraverseConditionalOperator(
    ConditionalOperator *C) {}

bool PluralMisuseChecker::MethodCrawler::VisitConditionalOperator(
    const ConditionalOperator *C) {}

void PluralMisuseChecker::MethodCrawler::reportPluralMisuseError(
    const Stmt *S) const {}

//===----------------------------------------------------------------------===//
// Checker registration.
//===----------------------------------------------------------------------===//

void ento::registerNonLocalizedStringChecker(CheckerManager &mgr) {}

bool ento::shouldRegisterNonLocalizedStringChecker(const CheckerManager &mgr) {}

void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) {}

bool ento::shouldRegisterEmptyLocalizationContextChecker(
                                                    const CheckerManager &mgr) {}

void ento::registerPluralMisuseChecker(CheckerManager &mgr) {}

bool ento::shouldRegisterPluralMisuseChecker(const CheckerManager &mgr) {}