<commit_before># $Id$
package Module::Extract::Use;
use strict;

use warnings;
no warnings;

use subs qw();
use vars qw($VERSION);

$VERSION = '0.13';

=head1 NAME

Module::Extract::Use - Pull out the modules a module uses

=head1 SYNOPSIS

	use Module::Extract::Use;

	my $extor = Module::Extract::Use->new;
	
	my @modules = $extor->get_modules( $file );
	if( $extor->error ) { ... }
	
	
=head1 DESCRIPTION

Extract the names of the modules used in a file using a static analysis.
Since this module does not run code, it cannot find dynamic uses of
modules, such as C<eval "require $class">.

=cut

=over 4

=item new

Makes an object. The object doesn't do anything just yet, but you
need it to call the methods.

=cut

sub new 
	{ 
	my $class = shift;
	
	my $self = bless {}, $class;
	
	$self->init;
	
	$self;
	}

=item init

Set up the object. You shouldn't need to call this yourself.

=cut

sub init 
	{ 
	$_[0]->_clear_error;
	}

=item get_modules( FILE )

Returns a list of namespaces explicity use-d in FILE. Returns undef if the
file does not exist or if it can't parse the file.

Each used namespace is only in the list even if it is used multiple times
in the file. The order of the list does not correspond to anything so don't
use the order to infer anything.

=cut

sub get_modules {
	my( $self, $file ) = @_;

	$_[0]->_clear_error;

	unless( -e $file )
		{
		$self->_set_error( ref( $self ) . ": File [$file] does not exist!" );
		return;
		}

	require PPI;

	my $Document = eval { PPI::Document->new( $file ) };
	unless( $Document )
		{
		$self->_set_error( ref( $self ) . ": Could not parse file [$file]" );
		return;
		}
		
	my $modules = $Document->find( 
		sub {
			$_[1]->isa( 'PPI::Statement::Include' )  && $_[1]->type eq 'use'
			}
		);
	
	my %Seen;
	my @modules = grep { ! $Seen{$_}++ } eval { map { $_->module } @$modules };

	@modules;
	}

=item error

Return the error from the last call to C<get_modules>.

=cut

sub _set_error   { $_[0]->{error} = $_[1]; }
	<commit_msg>* Find require-d modules too<commit_after># $Id$
package Module::Extract::Use;
use strict;

use warnings;
no warnings;

use subs qw();
use vars qw($VERSION);

$VERSION = '0.13';

=head1 NAME

Module::Extract::Use - Pull out the modules a module uses

=head1 SYNOPSIS

	use Module::Extract::Use;

	my $extor = Module::Extract::Use->new;
	
	my @modules = $extor->get_modules( $file );
	if( $extor->error ) { ... }
	
	
=head1 DESCRIPTION

Extract the names of the modules used in a file using a static analysis.
Since this module does not run code, it cannot find dynamic uses of
modules, such as C<eval "require $class">.

=cut

=over 4

=item new

Makes an object. The object doesn't do anything just yet, but you
need it to call the methods.

=cut

sub new 
	{ 
	my $class = shift;
	
	my $self = bless {}, $class;
	
	$self->init;
	
	$self;
	}

=item init

Set up the object. You shouldn't need to call this yourself.

=cut

sub init 
	{ 
	$_[0]->_clear_error;
	}

=item get_modules( FILE )

Returns a list of namespaces explicity use-d in FILE. Returns undef if the
file does not exist or if it can't parse the file.

Each used namespace is only in the list even if it is used multiple times
in the file. The order of the list does not correspond to anything so don't
use the order to infer anything.

=cut

sub get_modules {
	my( $self, $file ) = @_;

	$_[0]->_clear_error;

	unless( -e $file )
		{
		$self->_set_error( ref( $self ) . ": File [$file] does not exist!" );
		return;
		}

	require PPI;

	my $Document = eval { PPI::Document->new( $file ) };
	unless( $Document )
		{
		$self->_set_error( ref( $self ) . ": Could not parse file [$file]" );
		return;
		}
		
	my $modules = $Document->find( 
		sub {
			$_[1]->isa( 'PPI::Statement::Include' )  && 
				( $_[1]->type eq 'use' || $_[1]->type eq 'require' )
			}
		);
	
	my %Seen;
	my @modules = grep { ! $Seen{$_}++ } eval { map { $_->module } @$modules };

	@modules;
	}

=item error

Return the error from the last call to C<get_modules>.

=cut

sub _set_error   { $_[0]->{error} = $_[1]; }
	
{'fraction_non_alphanumeric': 0.14035520473606314, 'fraction_numerical': 0.006660088801184016, 'mean_word_length': 3.2864693446088795, 'pattern_counts': {'":': 4, '<': 7, '<?xml version=': 0, '>': 36, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2786821', 'n_tokens_mistral': 1541, 'n_tokens_neox': 1428, 'n_words': 537}