1 //===--- NSDateFormatterCheck.cpp - clang-tidy ----------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "NSDateFormatterCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
14 using namespace clang::ast_matchers
;
16 namespace clang::tidy::objc
{
18 void NSDateFormatterCheck::registerMatchers(MatchFinder
*Finder
) {
22 objcMessageExpr(hasSelector("setDateFormat:"),
23 hasReceiverType(asString("NSDateFormatter *")),
24 hasArgument(0, ignoringImpCasts(
25 objcStringLiteral().bind("str_lit")))),
29 static char ValidDatePatternChars
[] = {
30 'G', 'y', 'Y', 'u', 'U', 'r', 'Q', 'q', 'M', 'L', 'I', 'w', 'W', 'd',
31 'D', 'F', 'g', 'E', 'e', 'c', 'a', 'b', 'B', 'h', 'H', 'K', 'k', 'j',
32 'J', 'C', 'm', 's', 'S', 'A', 'z', 'Z', 'O', 'v', 'V', 'X', 'x'};
34 // Checks if the string pattern used as a date format specifier is valid.
35 // A string pattern is valid if all the letters(a-z, A-Z) in it belong to the
36 // set of reserved characters. See:
37 // https://www.unicode.org/reports/tr35/tr35.html#Invalid_Patterns
38 bool isValidDatePattern(StringRef Pattern
) {
39 return llvm::all_of(Pattern
, [](const auto &PatternChar
) {
40 return !isalpha(PatternChar
) ||
41 llvm::is_contained(ValidDatePatternChars
, PatternChar
);
45 // Checks if the string pattern used as a date format specifier contains
46 // any incorrect pattern and reports it as a warning.
47 // See: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
48 void NSDateFormatterCheck::check(const MatchFinder::MatchResult
&Result
) {
49 // Callback implementation.
50 const auto *StrExpr
= Result
.Nodes
.getNodeAs
<ObjCStringLiteral
>("str_lit");
51 const StringLiteral
*SL
= cast
<ObjCStringLiteral
>(StrExpr
)->getString();
52 StringRef SR
= SL
->getString();
54 if (!isValidDatePattern(SR
)) {
55 diag(StrExpr
->getExprLoc(), "invalid date format specifier");
58 if (SR
.contains('y') && SR
.contains('w') && !SR
.contains('Y')) {
59 diag(StrExpr
->getExprLoc(),
60 "use of calendar year (y) with week of the year (w); "
61 "did you mean to use week-year (Y) instead?");
63 if (SR
.contains('F')) {
64 if (!(SR
.contains('e') || SR
.contains('E'))) {
65 diag(StrExpr
->getExprLoc(),
66 "day of week in month (F) used without day of the week (e or E); "
67 "did you forget e (or E) in the format string?");
69 if (!SR
.contains('M')) {
70 diag(StrExpr
->getExprLoc(),
71 "day of week in month (F) used without the month (M); "
72 "did you forget M in the format string?");
75 if (SR
.contains('W') && !SR
.contains('M')) {
76 diag(StrExpr
->getExprLoc(), "Week of Month (W) used without the month (M); "
77 "did you forget M in the format string?");
79 if (SR
.contains('Y') && SR
.contains('Q') && !SR
.contains('y')) {
80 diag(StrExpr
->getExprLoc(),
81 "use of week year (Y) with quarter number (Q); "
82 "did you mean to use calendar year (y) instead?");
84 if (SR
.contains('Y') && SR
.contains('M') && !SR
.contains('y')) {
85 diag(StrExpr
->getExprLoc(),
86 "use of week year (Y) with month (M); "
87 "did you mean to use calendar year (y) instead?");
89 if (SR
.contains('Y') && SR
.contains('D') && !SR
.contains('y')) {
90 diag(StrExpr
->getExprLoc(),
91 "use of week year (Y) with day of the year (D); "
92 "did you mean to use calendar year (y) instead?");
94 if (SR
.contains('Y') && SR
.contains('W') && !SR
.contains('y')) {
95 diag(StrExpr
->getExprLoc(),
96 "use of week year (Y) with week of the month (W); "
97 "did you mean to use calendar year (y) instead?");
99 if (SR
.contains('Y') && SR
.contains('F') && !SR
.contains('y')) {
100 diag(StrExpr
->getExprLoc(),
101 "use of week year (Y) with day of the week in month (F); "
102 "did you mean to use calendar year (y) instead?");
106 } // namespace clang::tidy::objc