Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Perlの静的解析入門とPerlリファクタリングツールApp::PRTのご紹介

hitode909
August 30, 2014

 Perlの静的解析入門とPerlリファクタリングツールApp::PRTのご紹介

YAPC::Asia Tokyo 2014で発表しました

hitode909

August 30, 2014
Tweet

More Decks by hitode909

Other Decks in Technology

Transcript

  1. %FWFM3FGBDUPS my $regex1 = '(\W)(' . $old_name . ')(\W)'; my

    $regex2 = "^$old_name(" . '\W)'; while (<$fh>) { $line_number++; if (/$regex1/o or /$regex2/o) { my $new_line = $_; $new_line =~ s/$regex1/$1$new_name$3/g; $new_line =~ s/$regex2/$new_name$1/; ਖ਼نදݱͰஔ׵ 14೥8݄30೔౔༵೔
  2. %FWFM3FGBDUPS sub _syntax_check{ my $self = shift; my $tmp; my

    $eval_stmt = "my (". join ', ', @{$self->{parms}}; $eval_stmt .= ");\n"; $eval_stmt .= $self->get_sub_call(); $eval_stmt .= $self->get_new_code(); $self->{eval_code} = $eval_stmt; eval " $eval_stmt "; if ($@) { $self->{eval_err} = $@; FWBMͯ͠੒ޭ͔ͨ͠ݟΔ ͱʹ͔͘ϫΠϧυ 14೥8݄30೔౔༵೔
  3. TFE͸ਫ਼౓௿͍ my $blog = shift; print $blog->{id}; print $blog->{user_id}; my

    $blog = shift; print $blog->{article_id}; print $blog->{user_article_id}; sed -e “s/id/article_id/” 14೥8݄30೔౔༵೔
  4. ‘;’ ߏจ໦ ‘print’ ‘ ‘ List ‘ ‘ ‘ ‘

    ‘1’ ‘ ‘ ‘*’ ‘ ‘ ‘2’ ‘+’ ‘3’ Statement Document print (1 * 2) + 3; 14೥8݄30೔౔༵೔
  5. ‘;’ ߏจ໦ ‘print’ ‘ ‘ Statement Document print "(1 *

    2) + 3"; ‘"(1 * 2) + 3"’ 14೥8݄30೔౔༵೔
  6. 11*Ͱߏจ໦ݟ͍ͨ print (1 * 2) + 3; 11*%PDVNFOU 11*4UBUFNFOU 11*5PLFO8PSEQSJOU

    11*5PLFO8IJUFTQBDF 11*4USVDUVSF-JTU  11*4UBUFNFOU&YQSFTTJPO 11*5PLFO/VNCFS 11*5PLFO8IJUFTQBDF 11*5PLFO0QFSBUPS  11*5PLFO8IJUFTQBDF 11*5PLFO/VNCFS 11*5PLFO8IJUFTQBDF 11*5PLFO0QFSBUPS  11*5PLFO8IJUFTQBDF 11*5PLFO/VNCFS 11*5PLFO4USVDUVSF 11*5PLFO8IJUFTQBDFaO use PPI; use PPI::Dumper; my $document = PPI::Document->new($ARGV[0]); my $dumper = PPI::Dumper->new($document); $dumper->print; 14೥8݄30೔౔༵೔
  7. ‘;’ Ͱ͖ͨ ‘print’ ‘ ‘ List ‘ ‘ ‘ ‘

    ‘1’ ‘ ‘ ‘*’ ‘ ‘ ‘2’ ‘+’ ‘3’ Statement Document print (1 * 2) + 3; 14೥8݄30೔౔༵೔
  8. 11*Ͱ໋ྩ਺ݟ͍ͨ my $document = PPI::Document->new($ARGV[0]); my $statements = $document->find('PPI::Statement'); my

    $statement_count = @$statements; say "count: $statement_count"; 14೥8݄30೔౔༵೔
  9. 11*Ͱ'J[[#V[[Λഁյ͍ͨ͠ use PPI; my $path = $ARGV[0]; my $document =

    PPI::Document->new($path); my $tokens = $document->find('PPI::Token'); for my $token (@$tokens) { if (ref $token eq 'PPI::Token::Number') { $token->set_content($token->content * 2); } } $document->save("broken-$path"); 14೥8݄30೔౔༵೔
  10. 11*Ͱ'J[[#V[[Λഁյ͍ͨ͠ use PPI; my $path = $ARGV[0]; my $document =

    PPI::Document->new($path); my $tokens = $document->find('PPI::Token'); for my $token (@$tokens) { if (ref $token eq 'PPI::Token::Number') { $token->set_content($token->content * 2); } } $document->save("broken-$path"); ਺ࣈΛݟ͚ͭͨΒ ͱΓ͋͑ͣഒʹ͢Δ 14೥8݄30೔౔༵೔
  11. ͜Ε͕ use strict; use warnings; for my $i (1..20) {

    my $message = ""; $message .= "Fizz" unless $i % 3; $message .= "Buzz" unless $i % 5; $message = $i unless $message; print "$message\n"; } 14೥8݄30೔౔༵೔
  12. ͜͏ use strict; use warnings; for my $i (2..40) {

    my $message = ""; $message .= "Fizz" unless $i % 6; $message .= "Buzz" unless $i % 10; $message = $i unless $message; print "$message\n"; } 14೥8݄30೔౔༵೔
  13. -(5.  'J[[    #V[[  'J[[ 

        'J[[  #V[[    'J[[      'J[[#V[[      'J[[  14೥8݄30೔౔༵೔
  14. my $app = sub { return [ 200, [ 'Content-Type'

    => 'text/plain' ], [ 'Hello World' ] ]; }; 14೥8݄30೔౔༵೔
  15. my $app = sub { return [ 400, [ 'Content-Type'

    => 'text/plain' ], [ 'Hello World' ] ]; }; 14೥8݄30೔౔༵೔
  16. my $app = sub { return [ 400, [ 'Content-Type'

    => 'text/plain' ], [ 'Hello World' ] ]; }; ഒ 14೥8݄30೔౔༵೔
  17. งғؾ my $doc = PPI::Document->new($file); my $statements = $doc->find('PPI::Statement'); grep

    { my $tokens = [ $_->children ]; any { $_ eq $self->word; } @$tokens; } @$statements; 14೥8݄30೔౔༵೔
  18. package Antipop; use strict; use warnings; our $VERSION = '0.01';

    sub talk { return q{ҿΈʹߦͧ͘!!1}; } !!1; __END__ 14೥8݄30೔౔༵೔
  19. 1FSM$SJUJD1PMJDZ.PEVMFT3FRVJSF&OE8JUI0OF sub violates { my ( $self, $elem, $doc )

    = @_; # Last statement should be just "1;" my @significant = grep { _is_code($_) } $doc->schildren(); my $match = $significant[-1]; return if !$match; return if ((ref $match) eq 'PPI::Statement' && $match =~ m{\A 1 \s* ; \z}xms ); # Must be a violation... return $self->violation( $DESC, $EXPL, $match ); } 14೥8݄30೔౔༵೔
  20. Service::User->find_user_by_name($r, $name); sub find_user_by_name { my ($class, $r, $name) =

    @_; $r->db->select(..., $name); } CFGPSF 14೥8݄30೔౔༵೔
  21. Service::User->find_user_by_name($r->db, $name); sub find_user_by_name { my ($class, $db, $name) =

    @_; $db->select(..., $name); } ݺͼग़͠ݩͰ $r → $r->db ݺͼग़͠ઌͰ $r → $db $r->db → $db 14೥8݄30೔౔༵೔
  22. sub aaa { my ($class, $r = @_; AnotherService->bbb($r); $r->db->select(...,

    $user->name); } ΄͔ͷϝιουʹS౉ͯ͠Δ 14೥8݄30೔౔༵೔
  23. sub aaa { my ($class, $r) = @_; AnotherService->bbb($r->db); $r->db->select(...,

    $user->name); } ΄͔ͷϝιουʹS౉ͯ͠Δ ઌʹ"OPUIFS4FSWJDFΛ ϦϑΝΫλϦϯά 14೥8݄30೔౔༵೔
  24. sub aaa { my ($class, $db) = @_; AnotherService->bbb($db); $db->select(...,

    $user->name); } ΄͔ͷϝιουʹS౉ͯ͠Δ ઌʹ"OPUIFS4FSWJDFΛ ϦϑΝΫλϦϯά ϝιου಺͕શͯSECͳΒॻ͖׵͑ΒΕΔ 14೥8݄30೔౔༵೔
  25. sub aaa { my ($class, $r) = @_; my $user

    = $r->user; $r->db->select(..., $user->name); } ECҎ֎΋࢖ͬͯΔ 14೥8݄30೔౔༵೔
  26. sub aaa { my ($class, $r, $user) = @_; $r->db->select(...,

    $user->name); } ECҎ֎΋࢖ͬͯΔ VTFS΋ड͚औΔ ͜Ε͸ػցతʹ͸Ͱ͖ͳ͍ͷͰख࡞ۀ 14೥8݄30೔౔༵೔
  27. ࢦఆ͞ΕͨϑΝΠϧ ॱ൪ʹݟΔ sub replace_all { for my $file (@ARGV) {

    my $document = PPI::Document->new($file); my $package = $document->find_first( 'PPI::Statement::Package' ); my $package_name = $package->namespace; my $methods = $document->find( 'PPI::Statement::Sub' ); for my $method (@$methods) { my $document = PPI::Document->new($file); ...; } } } 14೥8݄30೔౔༵೔
  28. SECΛECʹஔ׵ sub r_db_to_db { my ($statement) = @_; my $tokens

    = [$statement->children]; for my $token (@$tokens) { if ($token eq '$r') { if ($token->next_token eq '->') { if ($token->next_token->next_token eq 'db') { $token->set_content('$db'); $token->next_token->set_content(''); $token->next_token->next_token->set_content(''); } else { die "->@{[ $token->next_token->next_token ]}"; } } else { die "@{[ $token->next_token ]}"; } } } } 14೥8݄30೔౔༵೔
  29. ݺͼग़͠ݩͷมߋ $command = 'prt replace_token \'' . $package_name . '->'

    . $method_name . '($r\' \'' . $package_name . '->' . $method_name . '($r->db\' --in- statement ' . $method_name . ' $(git grep --name-only ' . $method_name . ')'; system $command; 14೥8݄30೔౔༵೔
  30. Ϋϥε಺Ͱͷݺͼग़͠มߋ $command = 'prt replace_token \'$class' . '->' . $method_name

    . '($r\' \'$class' . '->' . $method_name . '($r->db\' --in- statement ' . $method_name . ' ' . $file; system $command; 14೥8݄30೔౔༵೔
  31. มߋՕॴΛςετͯ͠ίϛοτ if (`git diff --name-only t/**.t`) { system('carton exec --

    prove $(git diff -- name-only t/**.t)') and die "test failed"; } else { warn 'skip test'; } system "git commit -am '$package_name\ͷ $method_name\Λ\$db Λड͚औΔΑ͏ʹมߋ'"; system "git push"; 14೥8݄30೔౔༵೔