3535#include " diagnostic.h"
3636#include " input.h"
3737#include " rust-target.h"
38+ #include " selftest.h"
3839
3940extern bool
4041saw_errors (void );
@@ -54,7 +55,65 @@ const char *kHIRDumpFile = "gccrs.hir.dump";
5455const char *kHIRTypeResolutionDumpFile = " gccrs.type-resolution.dump" ;
5556const char *kTargetOptionsDumpFile = " gccrs.target-options.dump" ;
5657
57- const std::string kDefaultCrateName = " example" ;
58+ const std::string kDefaultCrateName = " rust_out" ;
59+ const size_t kMaxNameLength = 64 ;
60+
61+ static std::string
62+ infer_crate_name (const std::string &filename)
63+ {
64+ if (filename == " -" )
65+ return kDefaultCrateName ;
66+
67+ std::string crate = std::string (filename);
68+ size_t path_sep = crate.find_last_of (file_separator);
69+
70+ // find the base filename
71+ if (path_sep != std::string::npos)
72+ crate.erase (0 , path_sep + 1 );
73+
74+ // find the file stem name (remove file extension)
75+ size_t ext_position = crate.find_last_of (' .' );
76+ if (ext_position != std::string::npos)
77+ crate.erase (ext_position);
78+
79+ // Replace all the '-' symbols with '_' per Rust rules
80+ for (auto &c : crate)
81+ {
82+ if (c == ' -' )
83+ c = ' _' ;
84+ }
85+ return crate;
86+ }
87+
88+ /* Validate the crate name using the ASCII rules
89+ TODO: Support Unicode version of the rules */
90+
91+ static bool
92+ validate_crate_name (const std::string &crate_name, Error &error)
93+ {
94+ if (crate_name.empty ())
95+ {
96+ error = Error (Location (), " crate name cannot be empty" );
97+ return false ;
98+ }
99+ if (crate_name.length () > kMaxNameLength )
100+ {
101+ error = Error (Location (), " crate name cannot exceed %ld characters" ,
102+ kMaxNameLength );
103+ return false ;
104+ }
105+ for (auto &c : crate_name)
106+ {
107+ if (!(ISALNUM (c) || c == ' _' ))
108+ {
109+ error = Error (Location (),
110+ " invalid character %<%c%> in crate name: %<%s%>" , c,
111+ crate_name.c_str ());
112+ return false ;
113+ }
114+ }
115+ return true ;
116+ }
58117
59118// Implicitly enable a target_feature (and recursively enable dependencies).
60119void
@@ -311,10 +370,6 @@ Session::init ()
311370
312371 // setup backend to GCC GIMPLE
313372 backend = rust_get_backend ();
314-
315- // set the default crate name if crate name was unset
316- if (options.crate_name .empty ())
317- options.set_crate_name (kDefaultCrateName );
318373}
319374
320375/* Initialise default options. Actually called before handle_option, unlike init
@@ -347,7 +402,16 @@ Session::handle_option (
347402 case OPT_frust_crate_:
348403 // set the crate name
349404 if (arg != nullptr )
350- ret = options.set_crate_name (arg);
405+ {
406+ auto error = Error (Location (), std::string ());
407+ if ((ret = validate_crate_name (arg, error)))
408+ options.set_crate_name (arg);
409+ else
410+ {
411+ rust_assert (!error.message .empty ());
412+ error.emit_error ();
413+ }
414+ }
351415 else
352416 ret = false ;
353417 break ;
@@ -479,6 +543,32 @@ Session::enable_dump (std::string arg)
479543void
480544Session::parse_files (int num_files, const char **files)
481545{
546+ if (options.crate_name .empty ())
547+ {
548+ /* HACK: We use the first file to infer the crate name, which might be
549+ * incorrect: since rustc only allows one file to be supplied in the
550+ * command-line */
551+ auto filename = " -" ;
552+ if (num_files > 0 )
553+ filename = files[0 ];
554+
555+ auto crate_name = infer_crate_name (filename);
556+ Error error ((Location ()), std::string ());
557+ rust_debug (" inferred crate name: %s" , crate_name.c_str ());
558+ if (!validate_crate_name (crate_name, error))
559+ {
560+ // fake a linemapping so that we can show the filename
561+ linemap->start_file (filename, 0 );
562+ linemap->start_line (0 , 1 );
563+ error.emit_error ();
564+ rust_inform (linemap->get_location (0 ),
565+ " crate name inferred from this file" );
566+ linemap->stop ();
567+ return ;
568+ }
569+ options.set_crate_name (crate_name);
570+ }
571+
482572 auto mappings = Analysis::Mappings::get ();
483573 CrateNum crate_num = mappings->setup_crate_mappings (options.crate_name );
484574 mappings->set_current_crate (crate_num);
@@ -1121,3 +1211,34 @@ TargetOptions::enable_implicit_feature_reqs (std::string feature)
11211211 * - code generation
11221212 * - link */
11231213} // namespace Rust
1214+
1215+ #if CHECKING_P
1216+ namespace selftest {
1217+ void
1218+ rust_crate_name_validation_test (void )
1219+ {
1220+ auto error = Rust::Error (Location (), std::string ());
1221+ ASSERT_TRUE (Rust::validate_crate_name (" example" , error));
1222+ ASSERT_TRUE (Rust::validate_crate_name (" abcdefg_1234" , error));
1223+ ASSERT_TRUE (Rust::validate_crate_name (" 1" , error));
1224+ // FIXME: The next test does not pass as of current implementation
1225+ // ASSERT_TRUE (Rust::CompileOptions::validate_crate_name ("惊吓"));
1226+ // NOTE: - is not allowed in the crate name ...
1227+
1228+ ASSERT_FALSE (Rust::validate_crate_name (" abcdefg-1234" , error));
1229+ ASSERT_FALSE (Rust::validate_crate_name (" a+b" , error));
1230+ ASSERT_FALSE (Rust::validate_crate_name (" /a+b/" , error));
1231+
1232+ /* Tests for crate name inference */
1233+ ASSERT_EQ (Rust::infer_crate_name (" c.rs" ), " c" );
1234+ // NOTE: ... but - is allowed when in the filename
1235+ ASSERT_EQ (Rust::infer_crate_name (" a-b.rs" ), " a_b" );
1236+ ASSERT_EQ (Rust::infer_crate_name (" book.rs.txt" ), " book.rs" );
1237+ #if defined(HAVE_DOS_BASED_FILE_SYSTEM)
1238+ ASSERT_EQ (Rust::infer_crate_name (" a\\ c\\ a-b.rs" ), " a_b" );
1239+ #else
1240+ ASSERT_EQ (Rust::infer_crate_name (" a/c/a-b.rs" ), " a_b" );
1241+ #endif
1242+ }
1243+ } // namespace selftest
1244+ #endif // CHECKING_P
0 commit comments