Skip to content
This repository was archived by the owner on Jul 27, 2023. It is now read-only.

Commit 73517af

Browse files
committed
Avoid lexing magic command when not an identifier
1 parent e363fb8 commit 73517af

File tree

1 file changed

+36
-10
lines changed

1 file changed

+36
-10
lines changed

parser/src/lexer.rs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -538,19 +538,28 @@ where
538538
}
539539
}
540540

541-
fn lex_and_emit_magic_command(&mut self) {
541+
fn lex_and_emit_magic_command(&mut self) -> bool {
542542
let kind = match self.window[..2] {
543543
[Some(c1), Some(c2)] => {
544544
MagicKind::try_from([c1, c2]).map_or_else(|_| MagicKind::try_from(c1), Ok)
545545
}
546546
// When the escape character is the last character of the file.
547547
[Some(c), None] => MagicKind::try_from(c),
548-
_ => return,
548+
_ => return false,
549549
};
550550
if let Ok(kind) = kind {
551+
// If the magic prefix is not followed by a valid identifier start
552+
// we don't lex it as a magic command.
553+
if let Some(c) = self.window[usize::from(kind.prefix_len())] {
554+
if !self.is_identifier_start(c) {
555+
return false;
556+
}
557+
}
551558
let magic_command = self.lex_magic_command(kind);
552559
self.emit(magic_command);
560+
return true;
553561
}
562+
false
554563
}
555564

556565
/// Lex a string literal.
@@ -705,7 +714,9 @@ where
705714
}
706715
// https://github.com/ipython/ipython/blob/635815e8f1ded5b764d66cacc80bbe25e9e2587f/IPython/core/inputtransformer2.py#L345
707716
Some('%' | '!' | '?' | '/' | ';' | ',') if self.mode == Mode::Jupyter => {
708-
self.lex_and_emit_magic_command();
717+
if !self.lex_and_emit_magic_command() {
718+
break;
719+
}
709720
}
710721
Some('\x0C') => {
711722
// Form feed character!
@@ -948,9 +959,15 @@ where
948959
}
949960
}
950961
'%' => {
951-
if self.mode == Mode::Jupyter && self.nesting == 0 && self.last_token_is_equal {
952-
self.lex_and_emit_magic_command();
962+
let lexed_magic_command = if self.mode == Mode::Jupyter
963+
&& self.nesting == 0
964+
&& self.last_token_is_equal
965+
{
966+
self.lex_and_emit_magic_command()
953967
} else {
968+
false
969+
};
970+
if !lexed_magic_command {
954971
let tok_start = self.get_pos();
955972
self.next_char();
956973
if let Some('=') = self.window[0] {
@@ -1032,9 +1049,15 @@ where
10321049
}
10331050
}
10341051
'!' => {
1035-
if self.mode == Mode::Jupyter && self.nesting == 0 && self.last_token_is_equal {
1036-
self.lex_and_emit_magic_command();
1052+
let lexed_magic_command = if self.mode == Mode::Jupyter
1053+
&& self.nesting == 0
1054+
&& self.last_token_is_equal
1055+
{
1056+
self.lex_and_emit_magic_command()
10371057
} else {
1058+
false
1059+
};
1060+
if !lexed_magic_command {
10381061
let tok_start = self.get_pos();
10391062
self.next_char();
10401063
if let Some('=') = self.window[0] {
@@ -1736,9 +1759,8 @@ baz = %matplotlib \
17361759

17371760
fn assert_no_jupyter_magic(tokens: &[Tok]) {
17381761
for tok in tokens {
1739-
match tok {
1740-
Tok::MagicCommand { .. } => panic!("Unexpected magic command token: {:?}", tok),
1741-
_ => {}
1762+
if let Tok::MagicCommand { .. } = tok {
1763+
panic!("Unexpected magic command token: {:?}", tok)
17421764
}
17431765
}
17441766
}
@@ -1751,6 +1773,10 @@ foo = /func
17511773
foo = ;func
17521774
foo = ,func
17531775
1776+
# Magic prefix is not followed by a valid identifier start
1777+
% timeit
1778+
foo = % timeit
1779+
17541780
(foo == %timeit a = b)
17551781
(foo := %timeit a = b)
17561782
def f(arg=%timeit a = b):

0 commit comments

Comments
 (0)