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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang-tools-extra/clang-tidy/objc/NSDateFormatterCheck.cpp