Skip to content

Commit 766a900

Browse files
committed
frust-cfg: Only allow double quoted values
This commit separates the `handle_cfg_option()` function in two, separating the parsing logic from the session logic. The parsing logic is able to be unit tested, and now only allows quoted values.
1 parent d81ba63 commit 766a900

File tree

7 files changed

+147
-77
lines changed

7 files changed

+147
-77
lines changed

gcc/rust/Make-lang.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ GRS_OBJS = \
6666
rust/rust-gcc.o \
6767
rust/rust-token.o \
6868
rust/rust-lex.o \
69+
rust/rust-cfg-parser.o \
6970
rust/rust-parse.o \
7071
rust/rust-ast-full-test.o \
7172
rust/rust-session-manager.o \

gcc/rust/parse/rust-cfg-parser.cc

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include "rust-cfg-parser.h"
2+
#include "selftest.h"
3+
4+
namespace Rust {
5+
bool
6+
parse_cfg_option (const std::string &input, std::string &key,
7+
std::string &value)
8+
{
9+
key.clear ();
10+
value.clear ();
11+
12+
auto equal = input.find ('=');
13+
14+
// If there is no equal sign, it means there is no value. Clean up the key
15+
// and return
16+
if (equal == std::string::npos)
17+
{
18+
key = input;
19+
20+
// FIXME: Make sure key is a proper identifier
21+
22+
return true;
23+
}
24+
25+
key = input.substr (0, equal);
26+
27+
auto remaining_input = input.substr (equal + 1);
28+
if (remaining_input[0] != '"' || remaining_input.back () != '"')
29+
return false;
30+
31+
// Remove the quotes around the value, by advancing one character
32+
value = remaining_input.substr (1);
33+
// And trimming the rightmost character. This is fine since we've already
34+
// checked that both the first and last characters were quotes.
35+
value.resize (value.size () - 1);
36+
37+
// FIXME: We need to sanitize here and make sure that both key and value
38+
// are proper identifiers
39+
40+
return true;
41+
}
42+
43+
} // namespace Rust
44+
45+
#if CHECKING_P
46+
47+
namespace selftest {
48+
49+
void
50+
rust_cfg_parser_test (void)
51+
{
52+
std::string key;
53+
std::string value;
54+
55+
ASSERT_TRUE (Rust::parse_cfg_option ("key-no-value", key, value));
56+
ASSERT_EQ (key, "key-no-value");
57+
ASSERT_TRUE (value.empty ());
58+
59+
ASSERT_TRUE (Rust::parse_cfg_option ("k=\"v\"", key, value));
60+
ASSERT_EQ (key, "k");
61+
ASSERT_EQ (value, "v");
62+
63+
// values should be between double quotes
64+
ASSERT_FALSE (Rust::parse_cfg_option ("k=v", key, value));
65+
66+
// No value is an error if there is an equal sign
67+
ASSERT_FALSE (Rust::parse_cfg_option ("k=", key, value));
68+
69+
// No key is an error
70+
ASSERT_FALSE (Rust::parse_cfg_option ("=", key, value));
71+
ASSERT_FALSE (Rust::parse_cfg_option ("=value", key, value));
72+
}
73+
} // namespace selftest
74+
75+
#endif // CHECKING_P

gcc/rust/parse/rust-cfg-parser.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* This file is part of GCC.
2+
3+
GCC is free software; you can redistribute it and/or modify
4+
it under the terms of the GNU General Public License as published by
5+
the Free Software Foundation; either version 3, or (at your option)
6+
any later version.
7+
8+
GCC is distributed in the hope that it will be useful,
9+
but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
GNU General Public License for more details.
12+
13+
You should have received a copy of the GNU General Public License
14+
along with GCC; see the file COPYING3. If not see
15+
<http://www.gnu.org/licenses/>. */
16+
17+
#ifndef RUST_CFG_PARSER_H
18+
#define RUST_CFG_PARSER_H
19+
20+
#include "config.h"
21+
#include "system.h"
22+
#include "coretypes.h"
23+
24+
#include <string>
25+
26+
namespace Rust {
27+
/**
28+
* Parse a `key` or `key="value"` pair given to the `-frust-cfg` compiler
29+
* option.
30+
*
31+
* The format is as follows:
32+
*
33+
* -frust-cfg=<input>
34+
*
35+
* cfg_input: identifier | identifier '=' '"' identifier '"'
36+
*
37+
* @param input User input given to the -frust-cfg option
38+
* @param key String in which to store the parsed `key`.
39+
* @param value String in which to store the parsed `value` if it exists
40+
*
41+
* @return false if the given input was invalid, true otherwise
42+
*/
43+
bool
44+
parse_cfg_option (const std::string &input, std::string &key,
45+
std::string &value);
46+
} // namespace Rust
47+
48+
#if CHECKING_P
49+
50+
namespace selftest {
51+
extern void
52+
rust_cfg_parser_test (void);
53+
} // namespace selftest
54+
55+
#endif // CHECKING_P
56+
57+
#endif // RUST_CFG_PARSER_H

gcc/rust/parse/rust-parse.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,5 +118,4 @@ extract_module_path (const AST::AttrVec &inner_attrs,
118118

119119
return path;
120120
}
121-
122121
} // namespace Rust

gcc/rust/rust-lang.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "langhooks.h"
3434
#include "langhooks-def.h"
3535
#include "selftest.h"
36+
#include "rust-cfg-parser.h"
3637

3738
#include <mpfr.h>
3839
// note: header files must be in this order or else forward declarations don't
@@ -453,6 +454,7 @@ run_rust_tests ()
453454
{
454455
// Call tests for the rust frontend here
455456
simple_assert ();
457+
rust_cfg_parser_test ();
456458
}
457459
} // namespace selftest
458460

gcc/rust/rust-session-manager.cc

Lines changed: 11 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "rust-tycheck-dump.h"
3030
#include "rust-ast-resolve-unused.h"
3131
#include "rust-compile.h"
32+
#include "rust-cfg-parser.h"
3233

3334
#include "diagnostic.h"
3435
#include "input.h"
@@ -382,87 +383,22 @@ Session::handle_cfg_option (const std::string &input)
382383
std::string key;
383384
std::string value;
384385

385-
enum pstate
386-
{
387-
KEY,
388-
EQ,
389-
VAL,
390-
DONE,
391-
ERROR
392-
};
393-
394-
// FIXME
395-
// we need to use the GCC self_test framework to unit-test this its
396-
// likely got a bunch of bugs. This simple parser could be extracted to a
397-
// helper function to be more easily unit-tested or it could be tested via
398-
// checking what the target_options contain
399-
bool expect_quote = false;
400-
pstate s = KEY;
401-
for (const auto &ch : input)
386+
// Refactor this if needed
387+
if (!parse_cfg_option (input, key, value))
402388
{
403-
if (ch == ' ')
404-
{
405-
if (!key.empty ())
406-
s = EQ;
407-
else if (!value.empty ())
408-
s = DONE;
409-
else
410-
{
411-
s = ERROR;
412-
break;
413-
}
414-
}
415-
else if (ch == '"')
416-
{
417-
expect_quote = !expect_quote;
418-
}
419-
else if (ch == '=')
420-
{
421-
if (key.empty ())
422-
{
423-
s = ERROR;
424-
break;
425-
}
426-
427-
s = VAL;
428-
}
429-
else
430-
{
431-
if (s == KEY)
432-
key.push_back (ch);
433-
else if (s == VAL)
434-
value.push_back (ch);
435-
else
436-
{
437-
s = ERROR;
438-
break;
439-
}
440-
}
441-
}
442-
443-
if (key.empty () && value.empty ())
444-
s = ERROR;
445-
446-
if (expect_quote)
447-
s = ERROR;
448-
449-
if (s == ERROR)
450-
{
451-
rust_error_at (Location (),
452-
"invalid %<-frust-cfg=option%> expected %<key%> or "
453-
"key=%<value%> got %<%s%>",
454-
input.c_str ());
389+
rust_error_at (
390+
Location (),
391+
"invalid argument to %<-frust-cfg%>: Accepted formats are "
392+
"%<-frust-cfg=key%> or %<-frust-cfg=key=\"value\"%> (quoted)");
455393
return false;
456394
}
457395

458396
if (value.empty ())
459-
{
460-
// rustc does not seem to error on dup key
461-
options.target_data.insert_key (key);
462-
return true;
463-
}
397+
// rustc does not seem to error on dup key
398+
options.target_data.insert_key (key);
399+
else
400+
options.target_data.insert_key_value_pair (key, value);
464401

465-
options.target_data.insert_key_value_pair (key, value);
466402
return true;
467403
}
468404

gcc/testsuite/rust/compile/cfg5.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// { dg-additional-options "-w -frust-cfg=A=B" }
1+
// { dg-additional-options "-w -frust-cfg=A=\"B\"" }
22
struct Foo;
33
impl Foo {
44
#[cfg(A = "B")]

0 commit comments

Comments
 (0)