t-rasmud created this revision.
t-rasmud added a reviewer: NoQ.
Herald added subscribers: carlosgalvezp, mgorny.
Herald added a project: All.
t-rasmud requested review of this revision.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.

Reports incorrect string patterns in the date format specifier.


https://reviews.llvm.org/D126097

Files:
  clang-tools-extra/clang-tidy/add_new_check.py
  clang-tools-extra/clang-tidy/objc/CMakeLists.txt
  clang-tools-extra/clang-tidy/objc/NsdateformatterCheck.cpp
  clang-tools-extra/clang-tidy/objc/NsdateformatterCheck.h
  clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  clang-tools-extra/docs/clang-tidy/checks/objc-NSDateFormatter.rst
  clang-tools-extra/test/clang-tidy/checkers/objc-NSDateFormatter.m

Index: clang-tools-extra/test/clang-tidy/checkers/objc-NSDateFormatter.m
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/objc-NSDateFormatter.m
@@ -0,0 +1,280 @@
+// RUN: %check_clang_tidy %s objc-NSDateFormatter %t
+
+#import <Foundation/Foundation.h>
+
+@interface TestClass : NSObject{
+}
++(void)testMethod1;
++(void)testMethod2;
++(void)testMethod3;
+@end
+
+@implementation TestClass
+
++(void)testMethod1{
+    // Reproducing incorrect behavior from Radar
+    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+    [formatter setDateFormat:@"YYYY_MM_dd_HH_mm_ss_SSS_ZZZ"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:17:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSDateComponents *comps = [[NSDateComponents alloc] init];
+    [comps setDay:29];
+    [comps setMonth:12];
+    [comps setYear:2014];
+    NSDate *date_radar = [[NSCalendar currentCalendar] dateFromComponents:comps];
+    NSLog(@"YYYY_MM_dd_HH_mm_ss_SSS_ZZZ %@",[formatter stringFromDate:date_radar]);
+    
+    // Radar correct behavior
+    [formatter setDateFormat:@"yyyy_MM_dd_HH_mm_ss_SSS_ZZZ"];
+    NSLog(@"yyyy_MM_dd_HH_mm_ss_SSS_ZZZ %@",[formatter stringFromDate:date_radar]);
+    
+    // Radar correct behavior - week year
+    [formatter setDateFormat:@"YYYY_ww_dd_HH_mm_ss_SSS_ZZZ"];
+    NSLog(@"YYYY_ww_dd_HH_mm_ss_SSS_ZZZ %@",[formatter stringFromDate:date_radar]);
+    
+    // Radar incorrect behavior
+    [formatter setDateFormat:@"yyyy_ww_dd_HH_mm_ss_SSS_ZZZ"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:35:31: warning: use of calendar year(y) with week of the year(w); did you mean to use week-year(Y) instead? [objc-NSDateFormatter]
+    NSLog(@"yyyy_ww_dd_HH_mm_ss_SSS_ZZZ %@",[formatter stringFromDate:date_radar]);
+    
+    NSLog(@"==========================================");
+    // Correct
+    [formatter setDateFormat:@"yyyy_MM"];
+    NSLog(@"yyyy_MM %@",[formatter stringFromDate:date_radar]);
+    
+    // Correct
+    [formatter setDateFormat:@"yyyy_dd"];
+    NSLog(@"yyyy_dd %@",[formatter stringFromDate:date_radar]);
+    
+    // Correct
+    [formatter setDateFormat:@"yyyy_DD"];
+    NSLog(@"yyyy_DD %@",[formatter stringFromDate:date_radar]);
+    
+    // Incorrect
+    [formatter setDateFormat:@"yyyy_ww"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:53:31: warning: use of calendar year(y) with week of the year(w); did you mean to use week-year(Y) instead? [objc-NSDateFormatter]
+    NSLog(@"yyyy_ww %@",[formatter stringFromDate:date_radar]);
+    
+    // Incorrect
+    [formatter setDateFormat:@"yyyy_WW"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:58:31: warning: Week of Month(W) used without the month(M); did you forget M in the format string? [objc-NSDateFormatter]
+    NSLog(@"yyyy_WW %@",[formatter stringFromDate:date_radar]);
+    
+    NSLog(@"==========================================");
+    // Correct
+    [formatter setDateFormat:@"yyyy_MM_dd"];
+    NSLog(@"yyyy_MM_dd %@",[formatter stringFromDate:date_radar]);
+    
+    // Potentially Incorrect
+    [formatter setDateFormat:@"yyyy_MM_DD"];
+    NSLog(@"yyyy_MM_DD %@",[formatter stringFromDate:date_radar]);
+    
+    // Incorrect
+    [formatter setDateFormat:@"yyyy_MM_ww"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:72:31: warning: use of calendar year(y) with week of the year(w); did you mean to use week-year(Y) instead? [objc-NSDateFormatter]
+    NSLog(@"yyyy_MM_ww %@",[formatter stringFromDate:date_radar]);
+    
+    NSLog(@"=======WEEK YEAR==========");
+    // Incorrect
+    [formatter setDateFormat:@"YYYY_MM"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:78:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_MM %@",[formatter stringFromDate:date_radar]);
+    
+    // Correct
+    [formatter setDateFormat:@"YYYY_ww"];
+    NSLog(@"YYYY_ww %@",[formatter stringFromDate:date_radar]);
+    
+    // Incorrect
+    [formatter setDateFormat:@"YYYY_WW"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:87:31: warning: Week of Month(W) used without the month(M); did you forget M in the format string? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:87:31: warning: use of week year(Y) with week of the month(W); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_WW %@",[formatter stringFromDate:date_radar]);
+    
+    // Correct
+    [formatter setDateFormat:@"YYYY_dd"];
+    NSLog(@"YYYY_dd %@",[formatter stringFromDate:date_radar]);
+    
+    // Incorrect
+    [formatter setDateFormat:@"YYYY_DD"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:97:31: warning: use of week year(Y) with day of the year(D); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_DD %@",[formatter stringFromDate:date_radar]);
+    
+    NSLog(@"==========================================");
+    // Potentially Incorrect
+    [formatter setDateFormat:@"YYYY_ww_dd"];
+    NSLog(@"YYYY ww dd %@",[formatter stringFromDate:date_radar]);
+    
+    // Incorrect
+    [formatter setDateFormat:@"YYYY_ww_DD"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:107:31: warning: use of week year(Y) with day of the year(D); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_ww_DD %@",[formatter stringFromDate:date_radar]);
+    
+    // Generating 2015-W01-1 : How?
+}
+
++(void)testMethod2{
+    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+    NSDateComponents *comps = [[NSDateComponents alloc] init];
+    [comps setDay:29];
+    [comps setMonth:12];
+    [comps setYear:2014];
+    NSDate *date_radar = [[NSCalendar currentCalendar] dateFromComponents:comps];
+    
+    // Test 1 : incorrect
+    [formatter setDateFormat:@"yyyy_QQ_MM_ww_dd_EE"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:123:31: warning: use of calendar year(y) with week of the year(w); did you mean to use week-year(Y) instead? [objc-NSDateFormatter]
+    NSLog(@"yyyy_QQ_MM_ww_dd_EE %@",[formatter stringFromDate:date_radar]);
+    
+    // Test 2 : incorrect
+    [formatter setDateFormat:@"yyyy_QQ_MM_ww_dd_ee"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:128:31: warning: use of calendar year(y) with week of the year(w); did you mean to use week-year(Y) instead? [objc-NSDateFormatter]
+    NSLog(@"yyyy_QQ_MM_ww_dd_ee %@",[formatter stringFromDate:date_radar]);
+
+    // Test 3 : incorrect
+    [formatter setDateFormat:@"yyyy_QQ_MM_ww_DD_EE"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:133:31: warning: use of calendar year(y) with week of the year(w); did you mean to use week-year(Y) instead? [objc-NSDateFormatter]
+    NSLog(@"yyyy_QQ_MM_ww_DD_EE %@",[formatter stringFromDate:date_radar]);
+
+    // Test 4 : incorrect
+    [formatter setDateFormat:@"yyyy_QQ_MM_ww_DD_ee"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:138:31: warning: use of calendar year(y) with week of the year(w); did you mean to use week-year(Y) instead? [objc-NSDateFormatter]
+    NSLog(@"yyyy_QQ_MM_ww_DD_ee %@",[formatter stringFromDate:date_radar]);
+
+    // Test 5 : incorrect
+    [formatter setDateFormat:@"yyyy_QQ_MM_ww_F_EE"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:143:31: warning: use of calendar year(y) with week of the year(w); did you mean to use week-year(Y) instead? [objc-NSDateFormatter]
+    NSLog(@"yyyy_QQ_MM_ww_F_EE %@",[formatter stringFromDate:date_radar]);
+
+    // Test 6 : incorrect
+    [formatter setDateFormat:@"yyyy_QQ_MM_ww_F_ee"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:148:31: warning: use of calendar year(y) with week of the year(w); did you mean to use week-year(Y) instead? [objc-NSDateFormatter]
+    NSLog(@"yyyy_QQ_MM_ww_F_ee %@",[formatter stringFromDate:date_radar]);
+
+    // Test 7 : correct
+    [formatter setDateFormat:@"yyyy_QQ_MM_WW_dd_EE"];
+    NSLog(@"yyyy_QQ_MM_WW_dd_EE %@",[formatter stringFromDate:date_radar]);
+
+    // Test 8 : correct
+    [formatter setDateFormat:@"yyyy_QQ_MM_WW_dd_ee"];
+    NSLog(@"yyyy_QQ_MM_WW_dd_ee %@",[formatter stringFromDate:date_radar]);
+
+    // Test 9 : correct
+    [formatter setDateFormat:@"yyyy_QQ_MM_WW_DD_EE"];
+    NSLog(@"yyyy_QQ_MM_WW_DD_EE %@",[formatter stringFromDate:date_radar]);
+
+    // Test 10 : correct
+    [formatter setDateFormat:@"yyyy_QQ_MM_WW_DD_ee"];
+    NSLog(@"yyyy_QQ_MM_WW_DD_ee %@",[formatter stringFromDate:date_radar]);
+
+    // Test 11 : correct
+    [formatter setDateFormat:@"yyyy_QQ_MM_WW_F_EE"];
+    NSLog(@"yyyy_QQ_MM_WW_F_EE %@",[formatter stringFromDate:date_radar]);
+
+    // Test 12 : correct
+    [formatter setDateFormat:@"yyyy_QQ_MM_WW_F_ee"];
+    NSLog(@"yyyy_QQ_MM_WW_F_ee %@",[formatter stringFromDate:date_radar]);
+
+    // Test 13 : incorrect
+    [formatter setDateFormat:@"YYYY_QQ_MM_ww_dd_EE"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:177:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:177:31: warning: use of week year(Y) with quarter number(Q); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_QQ_MM_ww_dd_EE %@",[formatter stringFromDate:date_radar]);
+
+    // Test 14 : incorrect
+    [formatter setDateFormat:@"YYYY_QQ_MM_ww_dd_ee"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:183:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:183:31: warning: use of week year(Y) with quarter number(Q); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_QQ_MM_ww_dd_ee %@",[formatter stringFromDate:date_radar]);
+
+    // Test 15 : incorrect
+    [formatter setDateFormat:@"YYYY_QQ_MM_ww_DD_EE"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:189:31: warning: use of week year(Y) with day of the year(D); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:189:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-3]]:189:31: warning: use of week year(Y) with quarter number(Q); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_QQ_MM_ww_DD_EE %@",[formatter stringFromDate:date_radar]);
+
+    // Test 16 : incorrect
+    [formatter setDateFormat:@"YYYY_QQ_MM_ww_DD_ee"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:196:31: warning: use of week year(Y) with day of the year(D); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:196:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-3]]:196:31: warning: use of week year(Y) with quarter number(Q); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_QQ_MM_ww_DD_ee %@",[formatter stringFromDate:date_radar]);
+
+    // Test 17 : incorrect
+    [formatter setDateFormat:@"YYYY_QQ_MM_ww_F_EE"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:203:31: warning: use of week year(Y) with day of the week in month(F); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:203:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-3]]:203:31: warning: use of week year(Y) with quarter number(Q); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_QQ_MM_ww_F_EE %@",[formatter stringFromDate:date_radar]);
+
+    // Test 18 : incorrect
+    [formatter setDateFormat:@"YYYY_QQ_MM_ww_F_ee"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:210:31: warning: use of week year(Y) with day of the week in month(F); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:210:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-3]]:210:31: warning: use of week year(Y) with quarter number(Q); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_QQ_MM_ww_F_ee %@",[formatter stringFromDate:date_radar]);
+
+    // Test 19 : incorrect
+    [formatter setDateFormat:@"YYYY_QQ_MM_WW_dd_EE"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:217:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:217:31: warning: use of week year(Y) with quarter number(Q); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-3]]:217:31: warning: use of week year(Y) with week of the month(W); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_QQ_MM_WW_dd_EE %@",[formatter stringFromDate:date_radar]);
+
+    // Test 20 : incorrect
+    [formatter setDateFormat:@"YYYY_QQ_MM_WW_dd_ee"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:224:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:224:31: warning: use of week year(Y) with quarter number(Q); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-3]]:224:31: warning: use of week year(Y) with week of the month(W); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_QQ_MM_WW_dd_ee %@",[formatter stringFromDate:date_radar]);
+
+    // Test 21 : incorrect
+    [formatter setDateFormat:@"YYYY_QQ_MM_WW_DD_EE"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:231:31: warning: use of week year(Y) with day of the year(D); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:231:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-3]]:231:31: warning: use of week year(Y) with quarter number(Q); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-4]]:231:31: warning: use of week year(Y) with week of the month(W); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_QQ_MM_WW_DD_EE %@",[formatter stringFromDate:date_radar]);
+
+    // Test 22 : incorrect
+    [formatter setDateFormat:@"YYYY_QQ_MM_WW_DD_ee"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:239:31: warning: use of week year(Y) with day of the year(D); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:239:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-3]]:239:31: warning: use of week year(Y) with quarter number(Q); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-4]]:239:31: warning: use of week year(Y) with week of the month(W); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_QQ_MM_WW_DD_ee %@",[formatter stringFromDate:date_radar]);
+
+    // Test 23 : incorrect
+    [formatter setDateFormat:@"YYYY_QQ_MM_WW_F_EE"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:247:31: warning: use of week year(Y) with day of the week in month(F); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:247:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-3]]:247:31: warning: use of week year(Y) with quarter number(Q); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-4]]:247:31: warning: use of week year(Y) with week of the month(W); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_QQ_MM_WW_F_EE %@",[formatter stringFromDate:date_radar]);
+
+    // Test 24 : incorrect
+    [formatter setDateFormat:@"YYYY_QQ_MM_WW_F_ee"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:255:31: warning: use of week year(Y) with day of the week in month(F); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-2]]:255:31: warning: use of week year(Y) with month(M); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-3]]:255:31: warning: use of week year(Y) with quarter number(Q); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    // CHECK-MESSAGES: :[[@LINE-4]]:255:31: warning: use of week year(Y) with week of the month(W); did you mean to use calendar year(y) instead? [objc-NSDateFormatter]
+    NSLog(@"YYYY_QQ_MM_WW_F_ee %@",[formatter stringFromDate:date_radar]);
+}
+
++(void)testMethod3{
+    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+    NSDateComponents *comps = [[NSDateComponents alloc] init];
+    [comps setDay:29];
+    [comps setMonth:12];
+    [comps setYear:2014];
+    NSDate *date_radar = [[NSCalendar currentCalendar] dateFromComponents:comps];
+    
+    // Incorrect : has reserved and invalid chars
+    [formatter setDateFormat:@"Rashmi"];
+    // CHECK-MESSAGES: :[[@LINE-1]]:272:31: warning: invalid date format specifier [objc-NSDateFormatter]
+    NSLog(@"Rashmi %@",[formatter stringFromDate:date_radar]);
+    
+    // Correct
+    [formatter setDateFormat:@"AMy"];
+    NSLog(@"AMy %@",[formatter stringFromDate:date_radar]);
+}
+@end
Index: clang-tools-extra/docs/clang-tidy/checks/objc-NSDateFormatter.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/objc-NSDateFormatter.rst
@@ -0,0 +1,78 @@
+.. title:: clang-tidy - objc-NSDateFormatter
+
+objc-NSDateFormatter
+====================
+When ``NSDateFormatter`` is used to convert an ``NSDate`` type to a ``String`` type, the user
+can specify a custom format string. Certain format specifiers are undesirable
+despite being legal. See http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns for all legal date patterns.
+
+This checker reports as warnings the following string patterns in a date format specifier:
+1. yyyy + ww : Calendar year specified with week of a week year (unless YYYY is also specified).
+    Example 1:
+    Input Date: 29 December 2014 ; Format String: “yyyy-ww”
+    Output string: 2014-01 (Wrong because it’s not the first week of 2014)
+    
+    Example 2:
+    Input Date: 29 December 2014 ; Format String: “dd-MM-yyyy (ww-YYYY)”
+    Output string: 29-12-2014 (01-2015) (This is correct)
+    
+2. F without ee/EE : Numeric day of week in a month without actual day.
+    Example:
+    Input Date: 29 December 2014 ; Format String: “F-MM”
+    Output string: 5-12 (Wrong because it reads as *5th ___ of Dec* in English)
+    
+3. F without MM : Numeric day of week in a month without month.
+    Example:
+    Input Date: 29 December 2014 ; Format String: “F-EE”
+    Output string: 5-Mon (Wrong because it reads as *5th Mon of ___* in English)
+    
+4. WW without MM : Week of the month without the month.
+    Example:
+    Input Date: 29 December 2014 ; Format String: “WW-yyyy”
+    Output string: 05-2014 (Wrong because it reads as *5th Week of ___* in English)
+    
+5. YYYY + QQ : Week year specified with quarter of normal year (unless yyyy is also specified).
+    Example 1:
+    Input Date: 29 December 2014 ; Format String: “YYYY-QQ”
+    Output string: 2015-04 (Wrong because it’s not the 4th quarter of 2015)
+    
+    Example 2:
+    Input Date: 29 December 2014 ; Format String: “ww-YYYY (QQ-yyyy)”
+    Output string: 01-2015 (04-2014) (This is correct)
+    
+6. YYYY + MM :  Week year specified with Month of a calendar year (unless yyyy is also specified).
+    Example 1:
+    Input Date: 29 December 2014 ; Format String: “YYYY-MM”
+    Output string: 2015-12 (Wrong because it’s not the 12th month of 2015)
+    
+    Example 2:
+    Input Date: 29 December 2014 ; Format String: “ww-YYYY (MM-yyyy)”
+    Output string: 01-2015 (12-2014) (This is correct)
+    
+7. YYYY + DD : Week year with day of a calendar year (unless yyyy is also specified).
+    Example 1:
+    Input Date: 29 December 2014 ; Format String: “YYYY-DD”
+    Output string: 2015-363 (Wrong because it’s not the 363rd day of 2015)
+    
+    Example 2:
+    Input Date: 29 December 2014 ; Format String: “ww-YYYY (DD-yyyy)”
+    Output string: 01-2015 (363-2014) (This is correct)
+    
+8. YYYY + WW : Week year with week of a calendar year (unless yyyy is also specified).
+    Example 1:
+    Input Date: 29 December 2014 ; Format String: “YYYY-WW”
+    Output string: 2015-05 (Wrong because it’s not the 5th week of 2015)
+    
+    Example 2:
+    Input Date: 29 December 2014 ; Format String: “ww-YYYY (WW-MM-yyyy)”
+    Output string: 01-2015 (05-12-2014) (This is correct)
+    
+9. YYYY + F : Week year with day of week in a calendar month (unless yyyy is also specified).
+    Example 1:
+    Input Date: 29 December 2014 ; Format String: “YYYY-ww-F-EE”
+    Output string: 2015-01-5-Mon (Wrong because it’s not the 5th Monday of January in 2015)
+    
+    Example 2:
+    Input Date: 29 December 2014 ; Format String: “ww-YYYY (F-EE-MM-yyyy)”
+    Output string: 01-2015 (5-Mon-12-2014) (This is correct)
+
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -84,7 +84,7 @@
    `bugprone-posix-return <bugprone-posix-return.html>`_, "Yes"
    `bugprone-redundant-branch-condition <bugprone-redundant-branch-condition.html>`_, "Yes"
    `bugprone-reserved-identifier <bugprone-reserved-identifier.html>`_, "Yes"
-   `bugprone-shared-ptr-array-mismatch <bugprone-shared-ptr-array-mismatch.html>`_, "Yes"
+   `bugprone-shared-ptr-array-mismatch <bugprone-shared-ptr-array-mismatch.html>`_,
    `bugprone-signal-handler <bugprone-signal-handler.html>`_,
    `bugprone-signed-char-misuse <bugprone-signed-char-misuse.html>`_,
    `bugprone-sizeof-container <bugprone-sizeof-container.html>`_,
@@ -105,7 +105,7 @@
    `bugprone-terminating-continue <bugprone-terminating-continue.html>`_, "Yes"
    `bugprone-throw-keyword-missing <bugprone-throw-keyword-missing.html>`_,
    `bugprone-too-small-loop-variable <bugprone-too-small-loop-variable.html>`_,
-   `bugprone-unchecked-optional-access <bugprone-unchecked-optional-access.html>`_, "Yes"
+   `bugprone-unchecked-optional-access <bugprone-unchecked-optional-access.html>`_,
    `bugprone-undefined-memory-manipulation <bugprone-undefined-memory-manipulation.html>`_,
    `bugprone-undelegated-constructor <bugprone-undelegated-constructor.html>`_,
    `bugprone-unhandled-exception-at-new <bugprone-unhandled-exception-at-new.html>`_,
@@ -114,7 +114,7 @@
    `bugprone-unused-return-value <bugprone-unused-return-value.html>`_,
    `bugprone-use-after-move <bugprone-use-after-move.html>`_,
    `bugprone-virtual-near-miss <bugprone-virtual-near-miss.html>`_, "Yes"
-   `cert-dcl21-cpp <cert-dcl21-cpp.html>`_, "Yes"
+   `cert-dcl21-cpp <cert-dcl21-cpp.html>`_,
    `cert-dcl50-cpp <cert-dcl50-cpp.html>`_,
    `cert-dcl58-cpp <cert-dcl58-cpp.html>`_,
    `cert-env33-c <cert-env33-c.html>`_,
@@ -263,6 +263,7 @@
    `modernize-use-using <modernize-use-using.html>`_, "Yes"
    `mpi-buffer-deref <mpi-buffer-deref.html>`_, "Yes"
    `mpi-type-mismatch <mpi-type-mismatch.html>`_, "Yes"
+   `objc-NSDateFormatter <objc-NSDateFormatter.html>`_, "Yes"
    `objc-assert-equals <objc-assert-equals.html>`_, "Yes"
    `objc-avoid-nserror-init <objc-avoid-nserror-init.html>`_,
    `objc-dealloc-in-category <objc-dealloc-in-category.html>`_,
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -124,6 +124,11 @@
 
   Replaces groups of adjacent macros with an unscoped anonymous enum.
 
+- New :doc:`objc-NSDateFormatter
+  <clang-tidy/checks/objc-NSDateFormatter>` check.
+
+  Report incorrect string patterns in the date format specifier.
+
 - New :doc:`portability-std-allocator-const <clang-tidy/checks/portability-std-allocator-const>` check.
 
   Report use of ``std::vector<const T>`` (and similar containers of const
Index: clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp
+++ clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp
@@ -15,6 +15,7 @@
 #include "ForbiddenSubclassingCheck.h"
 #include "MissingHashCheck.h"
 #include "NSInvocationArgumentLifetimeCheck.h"
+#include "NsdateformatterCheck.h"
 #include "PropertyDeclarationCheck.h"
 #include "SuperSelfCheck.h"
 
@@ -27,6 +28,8 @@
 class ObjCModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<NsdateformatterCheck>(
+        "objc-NSDateFormatter");
     CheckFactories.registerCheck<AvoidNSErrorInitCheck>(
         "objc-avoid-nserror-init");
     CheckFactories.registerCheck<AssertEquals>("objc-assert-equals");
Index: clang-tools-extra/clang-tidy/objc/NsdateformatterCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/objc/NsdateformatterCheck.h
@@ -0,0 +1,35 @@
+//===--- NsdateformatterCheck.h - clang-tidy --------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_NSDATEFORMATTERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_NSDATEFORMATTERCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace objc {
+
+/// Checks the string pattern used as a date format specifier and reports warnings if
+/// it contains any incorrect sub-pattern.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/objc-NSDateFormatter.html
+class NsdateformatterCheck : public ClangTidyCheck {
+public:
+  NsdateformatterCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace objc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_NSDATEFORMATTERCHECK_H
Index: clang-tools-extra/clang-tidy/objc/NsdateformatterCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/objc/NsdateformatterCheck.cpp
@@ -0,0 +1,96 @@
+//===--- NsdateformatterCheck.cpp - clang-tidy ----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "NsdateformatterCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+# include <ctype.h>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace objc {
+
+void NsdateformatterCheck::registerMatchers(MatchFinder *Finder) {
+    // Adding matchers.
+    Finder->addMatcher(objcMessageExpr(hasSelector("setDateFormat:"), hasArgument(0, hasDescendant( stringLiteral().bind("str_lit")))), this);
+}
+
+// Checks if the string pattern used as a date format specifier is valid.
+// A string pattern is valid if all the letters(a-z, A-Z) in it belong to the set of reserved characters.
+// See: https://www.unicode.org/reports/tr35/tr35.html#Invalid_Patterns
+bool isValidDatePattern(StringRef Pattern) {
+    char ValidDatePatternChars[] = {'G', 'y', 'Y', 'u', 'U', 'r', 'Q', 'q', 'M', 'L', 'I', 'w', 'W', 'd', 'D', 'F', 'g', 'E', 'e', 'c', 'a', 'b', 'B', 'h', 'H', 'K', 'k', 'j', 'J', 'C', 'm', 's', 'S', 'A', 'z', 'Z', 'O', 'v', 'V', 'X', 'x'};
+    for (auto it = Pattern.begin(); it != Pattern.end(); it++) {
+        char PatternChar = *it;
+        if (isalpha(PatternChar)) {
+            if (std::find(std::begin(ValidDatePatternChars), std::end(ValidDatePatternChars), PatternChar) == std::end(ValidDatePatternChars))
+            {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+// Checks if the string pattern used as a date format specifier contains
+// any incorrect pattern and reports it as a warning.
+// See: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
+void NsdateformatterCheck::check(const MatchFinder::MatchResult &Result) {
+    // Callback implementation.
+    const auto *StrExpr = Result.Nodes.getNodeAs<ObjCStringLiteral>("str_lit");
+    StringRef SR = cast<StringLiteral>(StrExpr)->getString();
+    
+    if (!isValidDatePattern(SR)) {
+        diag(StrExpr->getExprLoc(), "invalid date format specifier");
+    }
+    
+    if (SR.contains('y') && SR.contains('w') && !SR.contains('Y')) {
+        diag(StrExpr->getExprLoc(), "use of calendar year(y) with week of the year(w); "
+             "did you mean to use week-year(Y) instead?");
+    }
+    if (SR.contains('F')) {
+        if (!(SR.contains('e') || SR.contains('E'))) {
+            diag(StrExpr->getExprLoc(), "day of week in month(F) used without day of the week(e or E); "
+                 "did you forget e (or E) in the format string?");
+        }
+        if (!SR.contains('M')) {
+            diag(StrExpr->getExprLoc(), "day of week in month(F) used without the month(M); "
+                 "did you forget M in the format string?");
+        }
+    }
+    if (SR.contains('W') && !SR.contains('M')) {
+        diag(StrExpr->getExprLoc(), "Week of Month(W) used without the month(M); "
+             "did you forget M in the format string?");
+    }
+    if (SR.contains('Y') && SR.contains('Q') && !SR.contains('y')) {
+        diag(StrExpr->getExprLoc(), "use of week year(Y) with quarter number(Q); "
+             "did you mean to use calendar year(y) instead?");
+    }
+    if (SR.contains('Y') && SR.contains('M') && !SR.contains('y')) {
+        diag(StrExpr->getExprLoc(), "use of week year(Y) with month(M); "
+             "did you mean to use calendar year(y) instead?");
+    }
+    if (SR.contains('Y') && SR.contains('D') && !SR.contains('y')) {
+        diag(StrExpr->getExprLoc(), "use of week year(Y) with day of the year(D); "
+             "did you mean to use calendar year(y) instead?");
+    }
+    if (SR.contains('Y') && SR.contains('W') && !SR.contains('y')) {
+        diag(StrExpr->getExprLoc(), "use of week year(Y) with week of the month(W); "
+             "did you mean to use calendar year(y) instead?");
+    }
+    if (SR.contains('Y') && SR.contains('F') && !SR.contains('y')) {
+        diag(StrExpr->getExprLoc(), "use of week year(Y) with day of the week in month(F); "
+             "did you mean to use calendar year(y) instead?");
+    }
+}
+
+} // namespace objc
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/objc/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/objc/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/objc/CMakeLists.txt
@@ -10,6 +10,7 @@
   ForbiddenSubclassingCheck.cpp
   MissingHashCheck.cpp
   NSInvocationArgumentLifetimeCheck.cpp
+  NsdateformatterCheck.cpp
   ObjCTidyModule.cpp
   PropertyDeclarationCheck.cpp
   SuperSelfCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to