Login
I think I've finally fixed the problem of false-positives coming from leading tabs...
authorJeffrey Ryan Thalhammer <jeff@imaginative-software.com>
Wed, 27 Aug 2008 17:46:06 +0000 (17:46 +0000)
committerJeffrey Ryan Thalhammer <jeff@imaginative-software.com>
Wed, 27 Aug 2008 17:46:06 +0000 (17:46 +0000)
lib/Perl/Critic/Policy/CodeLayout/ProhibitHardTabs.pm
t/20_policy_prohibithardtabs.t

index 34ee7e7..28e8893 100644 (file)
@@ -26,12 +26,11 @@ my $DEFAULT_ALLOW_LEADING_TABS = $TRUE;
 
 #-----------------------------------------------------------------------------
 
-# The following regex should be "qr{^ [^\t]+ \t}xms"
-# but it doesn't match when I expect it to.  I 
-# haven't figured out why.  So I used [^\s]
-# as a substitue to mean "not a tab".
+# The following regex should probably be "qr{^ .* [^\t]+ \t}xms" but it doesn't 
+# match when I expect it to.  I haven't figured out why, so I used "\S" to 
+# approximately mean "not a tab", and that seemd to work.
 
-my $NON_LEADING_TAB_REGEX = qr{^ [^\s]+ \t}xms;
+my $NON_LEADING_TAB_REGEX = qr{^ .* \S+ \t }xms;
 
 #-----------------------------------------------------------------------------
 
@@ -59,10 +58,19 @@ sub violates {
     # The __DATA__ element is exempt
     return if $elem->parent->isa('PPI::Statement::Data');
 
-    # Permit leading tabs, if allowed
-    return if $self->_allow_leading_tabs() && $elem !~ $NON_LEADING_TAB_REGEX;
-
-    # Must be a violation...
+    # If allowed, permit leading tabs in situations where whitespace s not significant.
+    if ( $self->_allow_leading_tabs() ) {
+        
+        return if $elem->location->[1] == 1;
+        
+        return if _is_extended_regex($elem)
+            && $elem !~ $NON_LEADING_TAB_REGEX;
+
+        return if $elem->isa('PPI::Token::QuoteLike::Words')
+            && $elem !~ $NON_LEADING_TAB_REGEX;
+    }
+    
+    # If we get here, then it must be a violation...
     return $self->violation( $DESC, $EXPL, $elem );
 }
 
@@ -70,10 +78,22 @@ sub violates {
 
 sub _allow_leading_tabs {
     my ( $self ) = @_;
-
     return $self->{_allow_leading_tabs};
 }
 
+#-----------------------------------------------------------------------------
+
+sub _is_extended_regex {
+    my ($elem) = @_;
+    
+    $elem->isa('PPI::Token::Regexp') 
+        || $elem->isa('PPI::Token::QuoteLike::Regexp')
+            || return;
+        
+   # Look for the /x modifier near the end
+   return $elem =~ m{\b [gimso]* x [gimso]* $}mx;
+}
+
 1;
 
 __END__
@@ -108,8 +128,11 @@ examined.
 
 =head1 CONFIGURATION
 
-Tabs in a leading position are allowed, but if you want to forbid all tabs
-everywhere, put this to your F<.perlcriticrc> file:
+Hard tabs in a string are always forbidden (use "\t" instead).  But 
+hard tabs in a leading position are allowed when they are used to indent
+code statements, C<qw()> word lists, and regular expressions with the C</x>
+modifier.  However, if you want to forbid all tabs everywhere, then add 
+this to your F<.perlcriticrc> file:
 
     [CodeLayout::ProhibitHardTabs]
     allow_leading_tabs = 0
index ccae538..0387446 100644 (file)
@@ -14,7 +14,7 @@ use warnings;
 # common P::C testing tools
 use Perl::Critic::TestUtils qw(pcritique fcritique);
 
-use Test::More tests => 7;
+use Test::More tests => 10;
 
 #-----------------------------------------------------------------------------
 
@@ -70,7 +70,21 @@ my \@list = qw(
 
 END_PERL
 
-is( pcritique($policy, \$code, \%config), 0, 'Tabs in qw()' );
+is( pcritique($policy, \$code, \%config), 0, 'Leading tabs in qw()' );
+
+#-----------------------------------------------------------------------------
+
+$code = <<"END_PERL";
+#This will be interpolated!
+
+my \@list = qw(
+\tfoo\tbar
+\tbaz\tnuts
+);
+
+END_PERL
+
+is( pcritique($policy, \$code, \%config), 1, 'Non-leading tabs in qw()' );
 
 #-----------------------------------------------------------------------------
 # RT #32440
@@ -82,9 +96,56 @@ $code = <<"END_PERL";
 \t(really | long)
 \tpattern
 /mx;
+
+#This will be interpolated!
+\$z = qr/
+\tsome
+\t(really | long)
+\tpattern
+/mx;
+
+END_PERL
+
+is( pcritique($policy, \$code, \%config), 0, 'Leading tabs in extended regex' );
+
+#-----------------------------------------------------------------------------
+# RT #32440
+
+$code = <<"END_PERL";
+#This will be interpolated!
+#Note that these regex does not have /x, so tabs are significant
+
+\$x =~ m/
+\tsome
+\tugly
+\tpattern
+/m;
+
+
+\$z = qr/
+\tsome
+\tugly
+\tpattern
+/gis;
+
+END_PERL
+
+is( pcritique($policy, \$code, \%config), 2, 'Leading tabs in non-extended regex' );
+
+#-----------------------------------------------------------------------------
+# RT #32440
+
+$code = <<"END_PERL";
+#This will be interpolated!
+#Note that these regex does not have /x, so tabs are significant
+
+\$x =~ m/
+\tsome\tugly\tpattern
+/xm;
+
 END_PERL
 
-is( pcritique($policy, \$code, \%config), 0, 'Tabs in regex' );
+is( pcritique($policy, \$code, \%config), 1, 'Non-leading tabs in extended regex' );
 
 #-----------------------------------------------------------------------------