35
35
#include " diagnostic.h"
36
36
#include " input.h"
37
37
#include " rust-target.h"
38
+ #include " selftest.h"
38
39
39
40
extern bool
40
41
saw_errors (void );
@@ -54,7 +55,65 @@ const char *kHIRDumpFile = "gccrs.hir.dump";
54
55
const char *kHIRTypeResolutionDumpFile = " gccrs.type-resolution.dump" ;
55
56
const char *kTargetOptionsDumpFile = " gccrs.target-options.dump" ;
56
57
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
+ }
58
117
59
118
// Implicitly enable a target_feature (and recursively enable dependencies).
60
119
void
@@ -311,10 +370,6 @@ Session::init ()
311
370
312
371
// setup backend to GCC GIMPLE
313
372
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 );
318
373
}
319
374
320
375
/* Initialise default options. Actually called before handle_option, unlike init
@@ -347,7 +402,16 @@ Session::handle_option (
347
402
case OPT_frust_crate_:
348
403
// set the crate name
349
404
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
+ }
351
415
else
352
416
ret = false ;
353
417
break ;
@@ -479,6 +543,32 @@ Session::enable_dump (std::string arg)
479
543
void
480
544
Session::parse_files (int num_files, const char **files)
481
545
{
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
+
482
572
auto mappings = Analysis::Mappings::get ();
483
573
CrateNum crate_num = mappings->setup_crate_mappings (options.crate_name );
484
574
mappings->set_current_crate (crate_num);
@@ -1121,3 +1211,34 @@ TargetOptions::enable_implicit_feature_reqs (std::string feature)
1121
1211
* - code generation
1122
1212
* - link */
1123
1213
} // 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