3714 lines
99 KiB
EmacsLisp
3714 lines
99 KiB
EmacsLisp
;;; rust-mode-tests.el --- ERT tests for rust-mode.el -*- lexical-binding: t; -*-
|
||
|
||
(require 'rust-mode)
|
||
(require 'ert)
|
||
(require 'cl-lib)
|
||
(require 'compile)
|
||
(require 'imenu)
|
||
|
||
(defconst rust-test-fill-column 32)
|
||
(setq-default indent-tabs-mode nil)
|
||
|
||
(defmacro rust-test-silence (messages &rest body)
|
||
`(let ((f (lambda (orig-fun format-string &rest args)
|
||
(unless (member format-string ,messages)
|
||
(apply orig-fun format-string args)))))
|
||
(unwind-protect
|
||
(progn
|
||
(advice-add 'message :around f)
|
||
,@body)
|
||
(advice-remove 'message f))))
|
||
|
||
(defun rust-compare-code-after-manip (_original _point-pos _manip-func expected got)
|
||
(equal expected got))
|
||
|
||
(defun rust-test-explain-bad-manip (original point-pos _manip-func expected got)
|
||
(if (equal expected got)
|
||
nil
|
||
(list
|
||
;; The (goto-char) and (insert) business here is just for
|
||
;; convenience--after an error, you can copy-paste that into emacs eval to
|
||
;; insert the bare strings into a buffer
|
||
"Rust code was manipulated wrong after:"
|
||
`(insert ,original)
|
||
`(goto-char ,point-pos)
|
||
'expected `(insert ,expected)
|
||
'got `(insert ,got)
|
||
(cl-loop for i from 0 to (max (length original) (length expected))
|
||
for oi = (if (< i (length got)) (elt got i))
|
||
for ei = (if (< i (length expected)) (elt expected i))
|
||
while (equal oi ei)
|
||
finally return `(first-difference-at
|
||
(goto-char ,(+ 1 i))
|
||
expected ,(char-to-string ei)
|
||
got ,(char-to-string oi))))))
|
||
(put 'rust-compare-code-after-manip 'ert-explainer
|
||
'rust-test-explain-bad-manip)
|
||
|
||
(defun rust-test-manip-code (original manip-pos manip-func expected &optional final-pos)
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(insert original)
|
||
(goto-char manip-pos)
|
||
(funcall manip-func)
|
||
(should (rust-compare-code-after-manip
|
||
original manip-pos manip-func expected (buffer-string)))
|
||
(if final-pos
|
||
(should (equal (point) final-pos)))))
|
||
|
||
(defmacro rust-test-with-standard-fill-settings (&rest body)
|
||
(declare (indent defun))
|
||
`(let ((fill-column rust-test-fill-column)
|
||
(sentence-end-double-space t)
|
||
(colon-double-space nil))
|
||
,@body))
|
||
|
||
(defun rust-test-fill-paragraph (unfilled expected &optional start-pos end-pos)
|
||
"We're going to run through many scenarios here--the point should be able to be anywhere from the start-pos (defaults to 1) through end-pos (defaults to the length of what was passed in) and (fill-paragraph) should return the same result. It should also work with fill-region from start-pos to end-pos.
|
||
|
||
Also, the result should be the same regardless of whether the code is at the beginning or end of the file. (If you're not careful, that can make a difference.) So we test each position given above with the passed code at the beginning, the end, neither and both. So we do this a total of 1 + (end-pos - start-pos)*4 times. Oy."
|
||
(let* ((start-pos (or start-pos 1))
|
||
(end-pos (or end-pos (length unfilled)))
|
||
(padding "\n \n")
|
||
(padding-len (length padding)))
|
||
(cl-loop
|
||
for pad-at-beginning from 0 to 1
|
||
do (cl-loop for pad-at-end from 0 to 1
|
||
with padding-beginning = (if (= 0 pad-at-beginning) "" padding)
|
||
with padding-end = (if (= 0 pad-at-end) "" padding)
|
||
with padding-adjust = (* padding-len pad-at-beginning)
|
||
;; If we're adding space to the beginning, and our start position
|
||
;; is at the very beginning, we want to test within the added space.
|
||
;; Otherwise adjust the start and end for the beginning padding.
|
||
with start-pos = (if (= 1 start-pos) 1 (+ padding-adjust start-pos))
|
||
with end-pos = (+ end-pos padding-adjust)
|
||
do (cl-loop for pos from start-pos to end-pos
|
||
do (rust-test-manip-code
|
||
(concat padding-beginning unfilled padding-end)
|
||
pos
|
||
(lambda ()
|
||
(rust-test-with-standard-fill-settings
|
||
(fill-paragraph)))
|
||
(concat padding-beginning expected padding-end)))))
|
||
;; In addition to all the fill-paragraph tests, check that it works using fill-region
|
||
(rust-test-manip-code
|
||
unfilled
|
||
start-pos
|
||
(lambda ()
|
||
(rust-test-with-standard-fill-settings
|
||
(fill-region start-pos end-pos)))
|
||
expected)
|
||
))
|
||
|
||
(ert-deftest fill-paragraph-top-level-multi-line-style-doc-comment-second-line ()
|
||
(rust-test-fill-paragraph
|
||
"/**
|
||
* This is a very very very very very very very long string
|
||
*/"
|
||
"/**
|
||
* This is a very very very very
|
||
* very very very long string
|
||
*/"))
|
||
|
||
|
||
(ert-deftest fill-paragraph-top-level-multi-line-style-doc-comment-first-line ()
|
||
(rust-test-fill-paragraph
|
||
"/** This is a very very very very very very very long string
|
||
*/"
|
||
"/** This is a very very very
|
||
* very very very very long
|
||
* string
|
||
*/"))
|
||
|
||
(ert-deftest fill-paragraph-multi-paragraph-multi-line-style-doc-comment ()
|
||
(let
|
||
((multi-paragraph-unfilled
|
||
"/**
|
||
* This is the first really really really really really really really long paragraph
|
||
*
|
||
* This is the second really really really really really really long paragraph
|
||
*/"))
|
||
(rust-test-fill-paragraph
|
||
multi-paragraph-unfilled
|
||
"/**
|
||
* This is the first really
|
||
* really really really really
|
||
* really really long paragraph
|
||
*
|
||
* This is the second really really really really really really long paragraph
|
||
*/"
|
||
1 89)
|
||
(rust-test-fill-paragraph
|
||
multi-paragraph-unfilled
|
||
"/**
|
||
* This is the first really really really really really really really long paragraph
|
||
*
|
||
* This is the second really
|
||
* really really really really
|
||
* really long paragraph
|
||
*/"
|
||
90)))
|
||
|
||
(ert-deftest fill-paragraph-multi-paragraph-single-line-style-doc-comment ()
|
||
(let
|
||
((multi-paragraph-unfilled
|
||
"/// This is the first really really really really really really really long paragraph
|
||
///
|
||
/// This is the second really really really really really really long paragraph"))
|
||
(rust-test-fill-paragraph
|
||
multi-paragraph-unfilled
|
||
"/// This is the first really
|
||
/// really really really really
|
||
/// really really long paragraph
|
||
///
|
||
/// This is the second really really really really really really long paragraph"
|
||
1 86)
|
||
(rust-test-fill-paragraph
|
||
multi-paragraph-unfilled
|
||
"/// This is the first really really really really really really really long paragraph
|
||
///
|
||
/// This is the second really
|
||
/// really really really really
|
||
/// really long paragraph"
|
||
87)))
|
||
|
||
(ert-deftest fill-paragraph-multi-paragraph-single-line-style-indented ()
|
||
(rust-test-fill-paragraph
|
||
" // This is the first really really really really really really really long paragraph
|
||
//
|
||
// This is the second really really really really really really long paragraph"
|
||
" // This is the first really
|
||
// really really really
|
||
// really really really
|
||
// long paragraph
|
||
//
|
||
// This is the second really really really really really really long paragraph" 1 89))
|
||
|
||
(ert-deftest fill-paragraph-multi-line-style-comment ()
|
||
(rust-test-fill-paragraph
|
||
"/* This is a very very very very very very very very long string
|
||
*/"
|
||
"/* This is a very very very very
|
||
* very very very very long
|
||
* string
|
||
*/"))
|
||
|
||
(ert-deftest fill-paragraph-multi-line-style-inner-doc-comment ()
|
||
(rust-test-fill-paragraph
|
||
"/*! This is a very very very very very very very long string
|
||
*/"
|
||
"/*! This is a very very very
|
||
* very very very very long
|
||
* string
|
||
*/"))
|
||
|
||
(ert-deftest fill-paragraph-single-line-style-inner-doc-comment ()
|
||
(rust-test-fill-paragraph
|
||
"//! This is a very very very very very very very long string"
|
||
"//! This is a very very very
|
||
//! very very very very long
|
||
//! string"))
|
||
|
||
(ert-deftest fill-paragraph-prefixless-multi-line-doc-comment ()
|
||
(rust-test-fill-paragraph
|
||
"/**
|
||
This is my summary. Blah blah blah blah blah. Dilly dally dilly dally dilly dally doo.
|
||
|
||
This is some more text. Fee fie fo fum. Humpty dumpty sat on a wall.
|
||
*/"
|
||
"/**
|
||
This is my summary. Blah blah
|
||
blah blah blah. Dilly dally
|
||
dilly dally dilly dally doo.
|
||
|
||
This is some more text. Fee fie fo fum. Humpty dumpty sat on a wall.
|
||
*/" 4 90))
|
||
|
||
(ert-deftest fill-paragraph-with-no-space-after-star-prefix ()
|
||
(rust-test-fill-paragraph
|
||
"/**
|
||
*This is a very very very very very very very long string
|
||
*/"
|
||
"/**
|
||
*This is a very very very very
|
||
*very very very long string
|
||
*/"))
|
||
|
||
(ert-deftest fill-paragraph-single-line-style-with-code-before ()
|
||
(rust-test-fill-paragraph
|
||
"fn foo() { }
|
||
/// This is my comment. This is more of my comment. This is even more."
|
||
"fn foo() { }
|
||
/// This is my comment. This is
|
||
/// more of my comment. This is
|
||
/// even more." 14))
|
||
|
||
(ert-deftest fill-paragraph-single-line-style-with-code-after ()
|
||
(rust-test-fill-paragraph
|
||
"/// This is my comment. This is more of my comment. This is even more.
|
||
fn foo() { }"
|
||
"/// This is my comment. This is
|
||
/// more of my comment. This is
|
||
/// even more.
|
||
fn foo() { }" 1 73))
|
||
|
||
(ert-deftest fill-paragraph-single-line-style-code-before-and-after ()
|
||
(rust-test-fill-paragraph
|
||
"fn foo() { }
|
||
/// This is my comment. This is more of my comment. This is even more.
|
||
fn bar() { }"
|
||
"fn foo() { }
|
||
/// This is my comment. This is
|
||
/// more of my comment. This is
|
||
/// even more.
|
||
fn bar() { }" 14 85))
|
||
|
||
(defun test-auto-fill (initial position inserted expected)
|
||
(rust-test-manip-code
|
||
initial
|
||
position
|
||
(lambda ()
|
||
(unwind-protect
|
||
(progn
|
||
(let ((fill-column rust-test-fill-column))
|
||
(auto-fill-mode)
|
||
(goto-char position)
|
||
(insert inserted)
|
||
(syntax-ppss-flush-cache 1)
|
||
(funcall auto-fill-function)))
|
||
(auto-fill-mode t)))
|
||
expected))
|
||
|
||
(ert-deftest auto-fill-multi-line-doc-comment ()
|
||
(test-auto-fill
|
||
"/**
|
||
*
|
||
*/"
|
||
7
|
||
" This is a very very very very very very very long string"
|
||
"/**
|
||
* This is a very very very very
|
||
* very very very long string
|
||
*/"))
|
||
|
||
(ert-deftest auto-fill-single-line-doc-comment ()
|
||
(test-auto-fill
|
||
"/// This is the first really
|
||
/// really really really really
|
||
/// really really long paragraph
|
||
///
|
||
/// "
|
||
103
|
||
"This is the second really really really really really really long paragraph"
|
||
"/// This is the first really
|
||
/// really really really really
|
||
/// really really long paragraph
|
||
///
|
||
/// This is the second really
|
||
/// really really really really
|
||
/// really long paragraph"
|
||
))
|
||
|
||
(ert-deftest auto-fill-multi-line-prefixless ()
|
||
(test-auto-fill
|
||
"/*
|
||
|
||
*/"
|
||
4
|
||
"This is a very very very very very very very long string"
|
||
"/*
|
||
This is a very very very very
|
||
very very very long string
|
||
*/"
|
||
))
|
||
|
||
(defun test-indent (indented &optional deindented)
|
||
(let ((deindented (or deindented (replace-regexp-in-string "^[[:blank:]]*" " " indented))))
|
||
(rust-test-manip-code
|
||
deindented
|
||
1
|
||
(lambda ()
|
||
(rust-test-silence
|
||
'("%s %s" ; "Indenting..." progress-reporter-do-update
|
||
"%sdone") ; "Indenting...done" progress-reporter-done
|
||
(indent-region 1 (+ 1 (buffer-size)))))
|
||
indented)))
|
||
|
||
|
||
(ert-deftest indent-struct-fields-aligned ()
|
||
(test-indent
|
||
"
|
||
struct Foo { bar: i32,
|
||
baz: i32 }
|
||
|
||
struct Blah {x:i32,
|
||
y:i32,
|
||
z:String"))
|
||
|
||
(ert-deftest indent-doc-comments ()
|
||
(test-indent
|
||
"
|
||
/**
|
||
* This is a doc comment
|
||
*
|
||
*/
|
||
|
||
/// So is this
|
||
|
||
fn foo() {
|
||
/*!
|
||
* this is a nested doc comment
|
||
*/
|
||
\n //! And so is this
|
||
}"))
|
||
|
||
(ert-deftest indent-inside-braces ()
|
||
(test-indent
|
||
"
|
||
// struct fields out one level:
|
||
struct foo {
|
||
a:i32,
|
||
// comments too
|
||
b:char
|
||
}
|
||
|
||
fn bar(x:Box<i32>) { // comment here should not affect the next indent
|
||
bla();
|
||
bla();
|
||
}"))
|
||
|
||
(ert-deftest indent-top-level ()
|
||
(test-indent
|
||
"
|
||
// Everything here is at the top level and should not be indented
|
||
#[attrib]
|
||
mod foo;
|
||
|
||
pub static bar = Quux{a: b()}
|
||
|
||
use foo::bar::baz;
|
||
|
||
fn foo() { }
|
||
"))
|
||
|
||
(ert-deftest font-lock-multi-raw-strings-in-a-row ()
|
||
(rust-test-font-lock
|
||
"
|
||
r\"foo\\\", \"bar\", r\"bar\";
|
||
r\"foo\\.\", \"bar\", r\"bar\";
|
||
r\"foo\\..\", \"bar\", r\"foo\\..\\bar\";
|
||
r\"\\\", \"foo\", r\"\\foo\";
|
||
not_a_string();
|
||
|
||
"
|
||
|
||
(apply #'append (mapcar (lambda (s) (list s 'font-lock-string-face))
|
||
'("r\"foo\\\"" "\"bar\"" "r\"bar\""
|
||
"r\"foo\\.\"" "\"bar\"" "r\"bar\""
|
||
"r\"foo\\..\"" "\"bar\"" "r\"foo\\..\\bar\""
|
||
"r\"\\\"" "\"foo\"" "r\"\\foo\"")))
|
||
))
|
||
|
||
(ert-deftest font-lock-raw-string-after-normal-string-ending-in-r ()
|
||
(rust-test-font-lock
|
||
"\"bar\" r\"foo\""
|
||
'("\"bar\"" font-lock-string-face "r\"foo\"" font-lock-string-face)))
|
||
|
||
(ert-deftest indent-params-no-align ()
|
||
(test-indent
|
||
"
|
||
// Indent out one level because no params appear on the first line
|
||
fn xyzzy(
|
||
a:i32,
|
||
b:char) { }
|
||
|
||
fn abcdef(
|
||
a:i32,
|
||
b:char)
|
||
-> char
|
||
{ }"))
|
||
|
||
(ert-deftest indent-params-align ()
|
||
(test-indent
|
||
"
|
||
// Align the second line of params to the first
|
||
fn foo(a:i32,
|
||
b:char) { }
|
||
|
||
fn bar( a:i32,
|
||
b:char)
|
||
-> i32
|
||
{ }
|
||
|
||
fn baz( a:i32, // should work with a comment here
|
||
b:char)
|
||
-> i32
|
||
{ }
|
||
"))
|
||
|
||
(ert-deftest indent-open-after-arrow ()
|
||
(test-indent
|
||
"
|
||
// Indent function body only one level after `-> {`
|
||
fn foo1(a:i32, b:char) -> i32 {
|
||
let body;
|
||
}
|
||
|
||
fn foo2(a:i32,
|
||
b:char) -> i32 {
|
||
let body;
|
||
}
|
||
|
||
fn foo3(a:i32,
|
||
b:char)
|
||
-> i32 {
|
||
let body;
|
||
}
|
||
|
||
fn foo4(a:i32,
|
||
b:char)
|
||
-> i32 where i32:A {
|
||
let body;
|
||
}
|
||
"))
|
||
|
||
(ert-deftest indent-return-type-non-visual ()
|
||
(let ((rust-indent-return-type-to-arguments nil))
|
||
(test-indent
|
||
"
|
||
fn imagine_long_enough_to_wrap_at_arrow(a:i32, b:char)
|
||
-> i32
|
||
{
|
||
let body;
|
||
}
|
||
")))
|
||
|
||
(ert-deftest indent-body-after-where ()
|
||
(let ((rust-indent-where-clause t))
|
||
(test-indent
|
||
"
|
||
fn foo1(a: A, b: B) -> A
|
||
where A: Clone + Default, B: Eq {
|
||
let body;
|
||
Foo {
|
||
bar: 3
|
||
}
|
||
}
|
||
|
||
fn foo2(a: A, b: B) -> A
|
||
where A: Clone + Default, B: Eq
|
||
{
|
||
let body;
|
||
Foo {
|
||
bar: 3
|
||
}
|
||
}
|
||
")))
|
||
|
||
(ert-deftest indent-align-where-clauses-style1a ()
|
||
(let ((rust-indent-where-clause t))
|
||
(test-indent
|
||
"
|
||
fn foo1a(a: A, b: B, c: C) -> D
|
||
where A: Clone + Default,
|
||
B: Eq,
|
||
C: PartialEq,
|
||
D: PartialEq {
|
||
let body;
|
||
Foo {
|
||
bar: 3
|
||
}
|
||
}
|
||
")))
|
||
|
||
(ert-deftest indent-align-where-clauses-style1b ()
|
||
(let ((rust-indent-where-clause t))
|
||
(test-indent
|
||
"
|
||
fn foo1b(a: A, b: B, c: C) -> D
|
||
where A: Clone + Default,
|
||
B: Eq,
|
||
C: PartialEq,
|
||
D: PartialEq
|
||
{
|
||
let body;
|
||
Foo {
|
||
bar: 3
|
||
}
|
||
}
|
||
")))
|
||
|
||
(ert-deftest indent-align-where-clauses-style2a ()
|
||
(test-indent
|
||
"
|
||
fn foo2a(a: A, b: B, c: C) -> D where A: Clone + Default,
|
||
B: Eq,
|
||
C: PartialEq,
|
||
D: PartialEq {
|
||
let body;
|
||
Foo {
|
||
bar: 3
|
||
}
|
||
}
|
||
"))
|
||
|
||
(ert-deftest indent-align-where-clauses-style2b ()
|
||
(test-indent
|
||
"
|
||
fn foo2b(a: A, b: B, c: C) -> D where A: Clone + Default,
|
||
B: Eq,
|
||
C: PartialEq,
|
||
D: PartialEq
|
||
{
|
||
let body;
|
||
Foo {
|
||
bar: 3
|
||
}
|
||
}
|
||
"))
|
||
|
||
(ert-deftest indent-align-where-clauses-style3a ()
|
||
(test-indent
|
||
"
|
||
fn foo3a(a: A, b: B, c: C) -> D where
|
||
A: Clone + Default,
|
||
B: Eq,
|
||
C: PartialEq,
|
||
D: PartialEq {
|
||
let body;
|
||
Foo {
|
||
bar: 3
|
||
}
|
||
}
|
||
"))
|
||
|
||
(ert-deftest indent-align-where-clauses-style3b ()
|
||
(test-indent
|
||
"
|
||
fn foo3b(a: A, b: B, c: C) -> D where
|
||
A: Clone + Default,
|
||
B: Eq,
|
||
C: PartialEq,
|
||
D: PartialEq
|
||
{
|
||
let body;
|
||
Foo {
|
||
bar: 3
|
||
}
|
||
}
|
||
"))
|
||
|
||
(ert-deftest indent-align-where-clauses-style4a ()
|
||
(let ((rust-indent-where-clause nil))
|
||
(test-indent
|
||
"
|
||
fn foo4a(a: A, b: B, c: C) -> D
|
||
where A: Clone + Default,
|
||
B: Eq,
|
||
C: PartialEq,
|
||
D: PartialEq {
|
||
let body;
|
||
Foo {
|
||
bar: 3
|
||
}
|
||
}
|
||
")))
|
||
|
||
(ert-deftest indent-align-where-clauses-style4b ()
|
||
(let ((rust-indent-where-clause nil))
|
||
(test-indent
|
||
"
|
||
fn foo4b(a: A, b: B, c: C) -> D
|
||
where A: Clone + Default,
|
||
B: Eq,
|
||
C: PartialEq,
|
||
D: PartialEq
|
||
{
|
||
let body;
|
||
Foo {
|
||
bar: 3
|
||
}
|
||
}
|
||
")))
|
||
|
||
(ert-deftest indent-align-where-clauses-impl-example ()
|
||
(let ((rust-indent-where-clause t))
|
||
(test-indent
|
||
"
|
||
impl<'a, K, Q: ?Sized, V, S> Index<&'a Q> for HashMap<K, V, S>
|
||
where K: Eq + Hash + Borrow<Q>,
|
||
Q: Eq + Hash,
|
||
S: HashState,
|
||
{
|
||
let body;
|
||
Foo {
|
||
bar: 3
|
||
}
|
||
}
|
||
")))
|
||
|
||
(ert-deftest indent-align-where-clauses-first-line ()
|
||
(let ((rust-indent-where-clause t))
|
||
(test-indent
|
||
"fn foo1(a: A, b: B) -> A
|
||
where A: Clone + Default, B: Eq {
|
||
let body;
|
||
Foo {
|
||
bar: 3
|
||
}
|
||
}
|
||
")))
|
||
|
||
(ert-deftest indent-align-where-in-comment1 ()
|
||
(test-indent
|
||
"/// - there must not exist an edge U->V in the graph where:
|
||
#[derive(Clone, PartialEq, Eq)]
|
||
pub struct Region { // <-- this should be flush with left margin!
|
||
entry: BasicBlockIndex,
|
||
leaves: BTreeMap<BasicBlockIndex, usize>,
|
||
}
|
||
"))
|
||
|
||
(ert-deftest indent-align-where-in-comment2 ()
|
||
(let ((rust-indent-where-clause t))
|
||
(test-indent
|
||
"fn foo<F,G>(f:F, g:G)
|
||
where F:Send,
|
||
// where
|
||
G:Sized
|
||
{
|
||
let body;
|
||
}
|
||
")))
|
||
|
||
(ert-deftest indent-align-where-in-comment3 ()
|
||
(let ((rust-indent-where-clause t))
|
||
(test-indent
|
||
"fn foo<F,G>(f:F, g:G)
|
||
where F:Send,
|
||
// where F:ThisIsNotActualCode,
|
||
G:Sized
|
||
{
|
||
let body;
|
||
}
|
||
")))
|
||
|
||
(ert-deftest indent-square-bracket-alignment ()
|
||
(test-indent
|
||
"
|
||
fn args_on_the_next_line( // with a comment
|
||
a:i32,
|
||
b:String) {
|
||
let aaaaaa = [
|
||
1,
|
||
2,
|
||
3];
|
||
let bbbbbbb = [1, 2, 3,
|
||
4, 5, 6];
|
||
let ccc = [ 10, 9, 8,
|
||
7, 6, 5];
|
||
}
|
||
"))
|
||
|
||
(ert-deftest indent-closing-square-bracket ()
|
||
(test-indent
|
||
"fn blergh() {
|
||
let list = vec![
|
||
1,
|
||
2,
|
||
3,
|
||
];
|
||
}"))
|
||
|
||
(ert-deftest indent-closing-paren ()
|
||
(test-indent
|
||
"fn blergh() {
|
||
call(
|
||
a,
|
||
function
|
||
);
|
||
}"))
|
||
|
||
(ert-deftest indent-nested-fns ()
|
||
(test-indent
|
||
"
|
||
fn nexted_fns(a: fn(b:i32,
|
||
c:char)
|
||
-> i32,
|
||
d: i32)
|
||
-> u128
|
||
{
|
||
0
|
||
}
|
||
"
|
||
))
|
||
|
||
(ert-deftest indent-multi-line-expr ()
|
||
(test-indent
|
||
"
|
||
fn foo()
|
||
{
|
||
x();
|
||
let a =
|
||
b();
|
||
}
|
||
"
|
||
))
|
||
|
||
(ert-deftest indent-match ()
|
||
(test-indent
|
||
"
|
||
fn foo() {
|
||
match blah {
|
||
Pattern => stuff(),
|
||
_ => whatever
|
||
}
|
||
}
|
||
"
|
||
))
|
||
|
||
(ert-deftest indent-match-multiline-pattern ()
|
||
(test-indent
|
||
"
|
||
fn foo() {
|
||
match blah {
|
||
Pattern |
|
||
Pattern2 => {
|
||
hello()
|
||
},
|
||
_ => whatever
|
||
}
|
||
}
|
||
"
|
||
))
|
||
|
||
(ert-deftest indent-indented-match ()
|
||
(test-indent
|
||
"
|
||
fn foo() {
|
||
let x =
|
||
match blah {
|
||
Pattern |
|
||
Pattern2 => {
|
||
hello()
|
||
},
|
||
_ => whatever
|
||
};
|
||
y();
|
||
}
|
||
"
|
||
))
|
||
|
||
(ert-deftest indent-curly-braces-within-parens ()
|
||
(test-indent
|
||
"
|
||
fn foo() {
|
||
let x =
|
||
foo(bar(|x| {
|
||
only_one_indent_here();
|
||
}));
|
||
y();
|
||
}
|
||
"
|
||
))
|
||
|
||
(ert-deftest indent-weirdly-indented-block ()
|
||
(rust-test-manip-code
|
||
"
|
||
fn foo() {
|
||
{
|
||
this_block_is_over_to_the_left_for_some_reason();
|
||
}
|
||
|
||
}
|
||
"
|
||
16
|
||
#'indent-for-tab-command
|
||
"
|
||
fn foo() {
|
||
{
|
||
this_block_is_over_to_the_left_for_some_reason();
|
||
}
|
||
|
||
}
|
||
"
|
||
))
|
||
|
||
(ert-deftest indent-multi-line-attrib ()
|
||
(test-indent
|
||
"
|
||
#[attrib(
|
||
this,
|
||
that,
|
||
theotherthing)]
|
||
fn function_with_multiline_attribute() {}
|
||
"
|
||
))
|
||
|
||
|
||
;; Make sure that in effort to cover match patterns we don't mistreat || or expressions
|
||
(ert-deftest indent-nonmatch-or-expression ()
|
||
(test-indent
|
||
"
|
||
fn foo() {
|
||
let x = foo() ||
|
||
bar();
|
||
}
|
||
"
|
||
))
|
||
|
||
;; Closing braces in single char literals and strings should not confuse the indentation
|
||
(ert-deftest indent-closing-braces-in-char-literals ()
|
||
(test-indent
|
||
"
|
||
fn foo() {
|
||
{ bar('}'); }
|
||
{ bar(']'); }
|
||
{ bar(')'); }
|
||
}
|
||
"
|
||
))
|
||
|
||
;; This is a test for #103: a comment after the last struct member that does
|
||
;; not have a trailing comma. The comment used to be indented one stop too
|
||
;; far.
|
||
(ert-deftest indent-comment-after-last-struct-member ()
|
||
(test-indent
|
||
"
|
||
struct A {
|
||
x: u8
|
||
// comment
|
||
}
|
||
|
||
struct A {
|
||
x: u8
|
||
/* comment */
|
||
}
|
||
"
|
||
))
|
||
|
||
(defconst rust-test-motion-string
|
||
"
|
||
fn fn1(arg: i32) -> bool {
|
||
let x = 5;
|
||
let y = b();
|
||
true
|
||
}
|
||
|
||
fn fn2(arg: i32) -> bool {
|
||
let x = 5;
|
||
let y = b();
|
||
true
|
||
}
|
||
|
||
pub fn fn3(arg: i32) -> bool {
|
||
let x = 5;
|
||
let y = b();
|
||
true
|
||
}
|
||
|
||
struct Foo {
|
||
x: i32
|
||
}
|
||
")
|
||
(defconst rust-test-region-string rust-test-motion-string)
|
||
(defconst rust-test-indent-motion-string
|
||
"
|
||
fn blank_line(arg:i32) -> bool {
|
||
|
||
}
|
||
|
||
fn indenting_closing_brace() {
|
||
if(true) {
|
||
}
|
||
}
|
||
|
||
fn indenting_middle_of_line() {
|
||
if(true) {
|
||
push_me_out();
|
||
} else {
|
||
pull_me_back_in();
|
||
}
|
||
}
|
||
|
||
fn indented_already() {
|
||
|
||
// The previous line already has its spaces
|
||
}
|
||
")
|
||
|
||
;; Symbol -> (line column)
|
||
(defconst rust-test-positions-alist '((start-of-fn1 (2 0))
|
||
(start-of-fn1-middle-of-line (2 15))
|
||
(middle-of-fn1 (3 7))
|
||
(end-of-fn1 (6 0))
|
||
(between-fn1-fn2 (7 0))
|
||
(start-of-fn2 (8 0))
|
||
(middle-of-fn2 (10 4))
|
||
(before-start-of-fn1 (1 0))
|
||
(after-end-of-fn2 (13 0))
|
||
(beginning-of-fn3 (14 0))
|
||
(middle-of-fn3 (16 4))
|
||
(middle-of-struct (21 10))
|
||
(before-start-of-struct (19 0))
|
||
(after-end-of-struct (23 0))
|
||
(blank-line-indent-start (3 0))
|
||
(blank-line-indent-target (3 4))
|
||
(closing-brace-indent-start (8 1))
|
||
(closing-brace-indent-target (8 5))
|
||
(middle-push-indent-start (13 2))
|
||
(middle-push-indent-target (13 9))
|
||
(after-whitespace-indent-start (13 1))
|
||
(after-whitespace-indent-target (13 8))
|
||
(middle-pull-indent-start (15 19))
|
||
(middle-pull-indent-target (15 12))
|
||
(blank-line-indented-already-bol-start (20 0))
|
||
(blank-line-indented-already-bol-target (20 4))
|
||
(blank-line-indented-already-middle-start (20 2))
|
||
(blank-line-indented-already-middle-target (20 4))
|
||
(nonblank-line-indented-already-bol-start (21 0))
|
||
(nonblank-line-indented-already-bol-target (21 4))
|
||
(nonblank-line-indented-already-middle-start (21 2))
|
||
(nonblank-line-indented-already-middle-target (21 4))))
|
||
|
||
(defun rust-get-buffer-pos (pos-symbol)
|
||
"Get buffer position from POS-SYMBOL.
|
||
|
||
POS-SYMBOL is a symbol found in `rust-test-positions-alist'.
|
||
Convert the line-column information from that list into a buffer position value."
|
||
(interactive "P")
|
||
(let* (
|
||
(line-and-column (cadr (assoc pos-symbol rust-test-positions-alist)))
|
||
(line (nth 0 line-and-column))
|
||
(column (nth 1 line-and-column)))
|
||
(save-excursion
|
||
(goto-char (point-min))
|
||
(forward-line (1- line))
|
||
(move-to-column column)
|
||
(point))))
|
||
|
||
;;; FIXME: Maybe add an ERT explainer function (something that shows the
|
||
;;; surrounding code of the final point, not just the position).
|
||
(defun rust-test-motion (source-code init-pos final-pos manip-func &rest args)
|
||
"Test that MANIP-FUNC moves point from INIT-POS to FINAL-POS.
|
||
|
||
If ARGS are provided, send them to MANIP-FUNC.
|
||
|
||
INIT-POS, FINAL-POS are position symbols found in `rust-test-positions-alist'."
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(insert source-code)
|
||
(goto-char (rust-get-buffer-pos init-pos))
|
||
(apply manip-func args)
|
||
(should (equal (point) (rust-get-buffer-pos final-pos)))))
|
||
|
||
(defun rust-test-region (source-code init-pos reg-beg reg-end manip-func &rest args)
|
||
"Test that MANIP-FUNC marks region from REG-BEG to REG-END.
|
||
|
||
INIT-POS is the initial position of point.
|
||
If ARGS are provided, send them to MANIP-FUNC.
|
||
All positions are position symbols found in `rust-test-positions-alist'."
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(insert source-code)
|
||
(goto-char (rust-get-buffer-pos init-pos))
|
||
(apply manip-func args)
|
||
(should (equal (list (region-beginning) (region-end))
|
||
(list (rust-get-buffer-pos reg-beg)
|
||
(rust-get-buffer-pos reg-end))))))
|
||
|
||
(ert-deftest rust-beginning-of-defun-from-middle-of-fn ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'middle-of-fn1
|
||
'start-of-fn1
|
||
#'beginning-of-defun))
|
||
|
||
(ert-deftest rust-beginning-of-defun-from-end ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'end-of-fn1
|
||
'start-of-fn1
|
||
#'beginning-of-defun))
|
||
|
||
(ert-deftest rust-beginning-of-defun-before-open-brace ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'start-of-fn1-middle-of-line
|
||
'start-of-fn1
|
||
#'beginning-of-defun))
|
||
|
||
(ert-deftest rust-beginning-of-defun-between-fns ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'between-fn1-fn2
|
||
'start-of-fn1
|
||
#'beginning-of-defun))
|
||
|
||
(ert-deftest rust-beginning-of-defun-with-arg ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'middle-of-fn2
|
||
'start-of-fn1
|
||
#'beginning-of-defun 2))
|
||
|
||
(ert-deftest rust-beginning-of-defun-with-negative-arg ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'middle-of-fn1
|
||
'beginning-of-fn3
|
||
#'beginning-of-defun -2))
|
||
|
||
(ert-deftest rust-beginning-of-defun-pub-fn ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'middle-of-fn3
|
||
'beginning-of-fn3
|
||
#'beginning-of-defun))
|
||
|
||
(ert-deftest rust-beginning-of-defun-string-comment ()
|
||
(let (fn-1 fn-2 p-1 p-2)
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(insert "fn test1() {
|
||
let s=r#\"
|
||
fn test2();
|
||
\"#;")
|
||
(setq p-1 (point))
|
||
(setq fn-1 (1+ p-1))
|
||
(insert "
|
||
fn test3() {
|
||
/*
|
||
fn test4();")
|
||
(setq p-2 (point))
|
||
(insert "\n*/\n}\n")
|
||
(setq fn-2 (point))
|
||
(insert "fn test5() { }")
|
||
|
||
(goto-char p-1)
|
||
(beginning-of-defun)
|
||
(should (eq (point) (point-min)))
|
||
|
||
(beginning-of-defun -2)
|
||
(should (eq (point) fn-2))
|
||
|
||
(goto-char p-2)
|
||
(beginning-of-defun)
|
||
(should (eq (point) fn-1))
|
||
|
||
(beginning-of-defun -1)
|
||
(should (eq (point) fn-2))
|
||
|
||
(goto-char (point-max))
|
||
(beginning-of-defun 2)
|
||
(should (eq (point) fn-1)))))
|
||
|
||
(ert-deftest rust-beginning-of-defun-pub-scoped ()
|
||
(let (fn-1-start fn-1-end fn-2-start fn-2-end)
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(setq fn-1-start (point))
|
||
(insert "pub(crate::mod1) fn test2() {}\n")
|
||
(setq fn-1-end (point))
|
||
(setq fn-2-start (point))
|
||
(insert "pub(self) fn test1() {}\n")
|
||
(setq fn-3-end (point))
|
||
|
||
(goto-char (point-max))
|
||
|
||
(beginning-of-defun)
|
||
(should (eq (point) fn-2-start))
|
||
|
||
(beginning-of-defun)
|
||
(should (eq (point) fn-1-start)))))
|
||
|
||
(ert-deftest rust-end-of-defun-from-middle-of-fn ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'middle-of-fn1
|
||
'between-fn1-fn2
|
||
#'end-of-defun))
|
||
|
||
(ert-deftest rust-end-of-defun-from-beg ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'start-of-fn1
|
||
'between-fn1-fn2
|
||
#'end-of-defun))
|
||
|
||
(ert-deftest rust-end-of-defun-before-open-brace ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'start-of-fn1-middle-of-line
|
||
'between-fn1-fn2
|
||
#'end-of-defun))
|
||
|
||
(ert-deftest rust-end-of-defun-between-fns ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'between-fn1-fn2
|
||
'after-end-of-fn2
|
||
#'end-of-defun))
|
||
|
||
(ert-deftest rust-end-of-defun-with-arg ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'middle-of-fn1
|
||
'after-end-of-fn2
|
||
#'end-of-defun 2))
|
||
|
||
(ert-deftest rust-end-of-defun-with-negative-arg ()
|
||
(rust-test-motion
|
||
rust-test-motion-string
|
||
'middle-of-fn3
|
||
'between-fn1-fn2
|
||
#'end-of-defun -2))
|
||
|
||
(ert-deftest rust-end-of-defun-pub-scoped ()
|
||
(let (fn-1-start fn-1-end fn-2-start fn-2-end)
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(setq fn-1-start (point))
|
||
(insert "pub(crate::mod1) fn test2() {}\n")
|
||
(setq fn-1-end (point))
|
||
(setq fn-2-start (point))
|
||
(insert "pub(self) fn test1() {}\n")
|
||
(setq fn-2-end (point))
|
||
|
||
(goto-char (point-min))
|
||
|
||
(end-of-defun)
|
||
(should (eq (point) fn-1-end))
|
||
|
||
(end-of-defun)
|
||
(should (eq (point) fn-2-end)))))
|
||
|
||
(ert-deftest rust-mark-defun-from-middle-of-fn ()
|
||
(rust-test-region
|
||
rust-test-region-string
|
||
'middle-of-fn2
|
||
'between-fn1-fn2 'after-end-of-fn2
|
||
#'mark-defun))
|
||
|
||
(ert-deftest rust-mark-defun-from-end ()
|
||
(rust-test-region
|
||
rust-test-region-string
|
||
'end-of-fn1
|
||
'before-start-of-fn1 'between-fn1-fn2
|
||
#'mark-defun))
|
||
|
||
(ert-deftest rust-mark-defun-start-of-defun ()
|
||
(rust-test-region
|
||
rust-test-region-string
|
||
'start-of-fn2
|
||
'between-fn1-fn2 'after-end-of-fn2
|
||
#'mark-defun))
|
||
|
||
(ert-deftest rust-mark-defun-from-middle-of-struct ()
|
||
(rust-test-region
|
||
rust-test-region-string
|
||
'middle-of-struct
|
||
'before-start-of-struct 'after-end-of-struct
|
||
#'mark-defun))
|
||
|
||
(ert-deftest indent-line-blank-line-motion ()
|
||
(rust-test-motion
|
||
rust-test-indent-motion-string
|
||
'blank-line-indent-start
|
||
'blank-line-indent-target
|
||
#'indent-for-tab-command))
|
||
|
||
(ert-deftest indent-line-closing-brace-motion ()
|
||
(rust-test-motion
|
||
rust-test-indent-motion-string
|
||
'closing-brace-indent-start
|
||
'closing-brace-indent-target
|
||
#'indent-for-tab-command))
|
||
|
||
(ert-deftest indent-line-middle-push-motion ()
|
||
(rust-test-motion
|
||
rust-test-indent-motion-string
|
||
'middle-push-indent-start
|
||
'middle-push-indent-target
|
||
#'indent-for-tab-command))
|
||
|
||
(ert-deftest indent-line-after-whitespace-motion ()
|
||
(rust-test-motion
|
||
rust-test-indent-motion-string
|
||
'after-whitespace-indent-start
|
||
'after-whitespace-indent-target
|
||
#'indent-for-tab-command))
|
||
|
||
(ert-deftest indent-line-middle-pull-motion ()
|
||
(rust-test-motion
|
||
rust-test-indent-motion-string
|
||
'middle-pull-indent-start
|
||
'middle-pull-indent-target
|
||
#'indent-for-tab-command))
|
||
|
||
(ert-deftest indent-line-blank-line-indented-already-bol ()
|
||
(rust-test-motion
|
||
rust-test-indent-motion-string
|
||
'blank-line-indented-already-bol-start
|
||
'blank-line-indented-already-bol-target
|
||
#'indent-for-tab-command))
|
||
|
||
(ert-deftest indent-line-blank-line-indented-already-middle ()
|
||
(rust-test-motion
|
||
rust-test-indent-motion-string
|
||
'blank-line-indented-already-middle-start
|
||
'blank-line-indented-already-middle-target
|
||
#'indent-for-tab-command))
|
||
|
||
(ert-deftest indent-line-nonblank-line-indented-already-bol ()
|
||
(rust-test-motion
|
||
rust-test-indent-motion-string
|
||
'nonblank-line-indented-already-bol-start
|
||
'nonblank-line-indented-already-bol-target
|
||
#'indent-for-tab-command))
|
||
|
||
(ert-deftest indent-line-nonblank-line-indented-already-middle ()
|
||
(rust-test-motion
|
||
rust-test-indent-motion-string
|
||
'nonblank-line-indented-already-middle-start
|
||
'nonblank-line-indented-already-middle-target
|
||
#'indent-for-tab-command))
|
||
|
||
(ert-deftest no-stack-overflow-in-rust-rewind-irrelevant ()
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(insert "fn main() {\n let x = 1;")
|
||
;; Insert 150 separate comments on the same line
|
||
(dotimes (_i 150)
|
||
(insert "/* foo */ "))
|
||
;; Rewinding from the last comment to the end of the let needs at least
|
||
;; 150 iterations, but if we limit the stack depth to 100 (this appears to
|
||
;; be some minimum), a recursive function would overflow, throwing an
|
||
;; error.
|
||
(let ((max-lisp-eval-depth 100))
|
||
(rust-rewind-irrelevant)
|
||
;; Only a non-stack overflowing function would make it this far. Also
|
||
;; check that we rewound till after the ;
|
||
(should (= (char-before) ?\;)))))
|
||
|
||
(defun rust-test-fontify-string (str)
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(insert str)
|
||
(font-lock-flush)
|
||
(font-lock-ensure)
|
||
(buffer-string)))
|
||
|
||
(defun rust-test-group-str-by-face (str)
|
||
"Fontify `STR' in rust-mode and group it by face, returning a
|
||
list of substrings of `STR' each followed by its face."
|
||
(cl-loop with fontified = (rust-test-fontify-string str)
|
||
for start = 0 then end
|
||
while start
|
||
for end = (next-single-property-change start 'face fontified)
|
||
for prop = (get-text-property start 'face fontified)
|
||
for text = (substring-no-properties fontified start end)
|
||
if prop
|
||
append (list text prop)))
|
||
|
||
(defun rust-test-font-lock (source face-groups)
|
||
"Test that `SOURCE' fontifies to the expected `FACE-GROUPS'"
|
||
(should (equal (rust-test-group-str-by-face source)
|
||
face-groups)))
|
||
|
||
(ert-deftest font-lock-attribute-simple ()
|
||
(rust-test-font-lock
|
||
"#[foo]"
|
||
'("#[foo]" font-lock-preprocessor-face)))
|
||
|
||
(ert-deftest font-lock-attribute-inner ()
|
||
(rust-test-font-lock
|
||
"#![foo]"
|
||
'("#![foo]" font-lock-preprocessor-face)))
|
||
|
||
(ert-deftest font-lock-attribute-key-value ()
|
||
(rust-test-font-lock
|
||
"#[foo = \"bar\"]"
|
||
'("#[foo = " font-lock-preprocessor-face
|
||
"\"bar\"" font-lock-string-face
|
||
"]" font-lock-preprocessor-face)))
|
||
|
||
(ert-deftest font-lock-attribute-around-comment ()
|
||
(rust-test-font-lock
|
||
"#[foo /* bar */]"
|
||
'("#[foo " font-lock-preprocessor-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"bar */" font-lock-comment-face
|
||
"]" font-lock-preprocessor-face)))
|
||
|
||
(ert-deftest font-lock-attribute-inside-string ()
|
||
(rust-test-font-lock
|
||
"\"#[foo]\""
|
||
'("\"#[foo]\"" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-attribute-inside-comment ()
|
||
(rust-test-font-lock
|
||
"/* #[foo] */"
|
||
'("/* " font-lock-comment-delimiter-face
|
||
"#[foo] */" font-lock-comment-face)))
|
||
|
||
(ert-deftest font-lock-number-with-type ()
|
||
(rust-test-font-lock
|
||
"-123i32"
|
||
'("i32" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"123u32"
|
||
'("u32" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"123_123_u32"
|
||
'("u32" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"0xff_u8"
|
||
'("u8" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"0b1111_1111_1001_0000i64"
|
||
'("i64" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"0usize"
|
||
'("usize" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"123.0f64 + 1."
|
||
'("f64" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"0.1f32"
|
||
'("f32" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"12E+99_f64"
|
||
'("f64" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"5f32"
|
||
'("f32" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"0x5i32"
|
||
'("i32" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"1x5i32"
|
||
'())
|
||
(rust-test-font-lock
|
||
"0x5i321"
|
||
'())
|
||
(rust-test-font-lock
|
||
"fname5f32"
|
||
'())
|
||
(rust-test-font-lock
|
||
"0x5i32+1"
|
||
'("i32" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"f(0xFFi32)"
|
||
'("i32" font-lock-type-face)))
|
||
|
||
(ert-deftest font-lock-double-quote-character-literal ()
|
||
(rust-test-font-lock
|
||
"'\"'; let"
|
||
'("'\"'" font-lock-string-face
|
||
"let" font-lock-keyword-face)))
|
||
|
||
(ert-deftest font-lock-fn-contains-capital ()
|
||
(rust-test-font-lock
|
||
"fn foo_Bar() {}"
|
||
'("fn" font-lock-keyword-face
|
||
"foo_Bar" font-lock-function-name-face)))
|
||
|
||
(ert-deftest font-lock-let-bindings ()
|
||
(rust-test-font-lock
|
||
"let foo;"
|
||
'("let" font-lock-keyword-face
|
||
"foo" font-lock-variable-name-face))
|
||
(rust-test-font-lock
|
||
"let ref foo;"
|
||
'("let" font-lock-keyword-face
|
||
"ref" font-lock-keyword-face
|
||
"foo" font-lock-variable-name-face))
|
||
(rust-test-font-lock
|
||
"let mut foo;"
|
||
'("let" font-lock-keyword-face
|
||
"mut" font-lock-keyword-face
|
||
"foo" font-lock-variable-name-face))
|
||
(rust-test-font-lock
|
||
"let foo = 1;"
|
||
'("let" font-lock-keyword-face
|
||
"foo" font-lock-variable-name-face))
|
||
(rust-test-font-lock
|
||
"let mut foo = 1;"
|
||
'("let" font-lock-keyword-face
|
||
"mut" font-lock-keyword-face
|
||
"foo" font-lock-variable-name-face))
|
||
(rust-test-font-lock
|
||
"fn foo() { let bar = 1; }"
|
||
'("fn" font-lock-keyword-face
|
||
"foo" font-lock-function-name-face
|
||
"let" font-lock-keyword-face
|
||
"bar" font-lock-variable-name-face))
|
||
(rust-test-font-lock
|
||
"fn foo() { let mut bar = 1; }"
|
||
'("fn" font-lock-keyword-face
|
||
"foo" font-lock-function-name-face
|
||
"let" font-lock-keyword-face
|
||
"mut" font-lock-keyword-face
|
||
"bar" font-lock-variable-name-face)))
|
||
|
||
(ert-deftest font-lock-ampersand ()
|
||
(rust-test-font-lock
|
||
"f(&a)"
|
||
'("&" rust-ampersand-face))
|
||
(rust-test-font-lock
|
||
"a && b &&& c"
|
||
nil)
|
||
(rust-test-font-lock
|
||
"&'a v"
|
||
'("&" rust-ampersand-face
|
||
"a" font-lock-variable-name-face))
|
||
(rust-test-font-lock
|
||
"&'static v"
|
||
'("&" rust-ampersand-face
|
||
"static" font-lock-keyword-face))
|
||
(rust-test-font-lock
|
||
"&mut v"
|
||
'("&" rust-ampersand-face
|
||
"mut" font-lock-keyword-face))
|
||
(rust-test-font-lock
|
||
"&f(&x)"
|
||
'("&" rust-ampersand-face
|
||
"&" rust-ampersand-face))
|
||
(rust-test-font-lock
|
||
"fn f(x: &X)"
|
||
'("fn" font-lock-keyword-face
|
||
"f" font-lock-function-name-face
|
||
"x" font-lock-variable-name-face
|
||
"&" rust-ampersand-face
|
||
"X" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"f(&X{x})"
|
||
'("&" rust-ampersand-face
|
||
"X" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"let x: &'_ f64 = &1.;"
|
||
'("let" font-lock-keyword-face
|
||
"x" font-lock-variable-name-face
|
||
"&" rust-ampersand-face
|
||
"_" font-lock-variable-name-face
|
||
"f64" font-lock-type-face
|
||
"&" rust-ampersand-face))
|
||
(rust-test-font-lock
|
||
"let x = &&1;"
|
||
'("let" font-lock-keyword-face
|
||
"x" font-lock-variable-name-face
|
||
"&&" rust-ampersand-face))
|
||
(rust-test-font-lock
|
||
"let x = &*y;"
|
||
'("let" font-lock-keyword-face
|
||
"x" font-lock-variable-name-face
|
||
"&" rust-ampersand-face))
|
||
(rust-test-font-lock
|
||
"let x = &::std::f64::consts::PI;"
|
||
'("let" font-lock-keyword-face
|
||
"x" font-lock-variable-name-face
|
||
"&" rust-ampersand-face
|
||
"std" font-lock-constant-face
|
||
"f64" font-lock-type-face
|
||
"consts" font-lock-constant-face
|
||
"PI" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"let x = &(1, 2);"
|
||
'("let" font-lock-keyword-face
|
||
"x" font-lock-variable-name-face
|
||
"&" rust-ampersand-face))
|
||
(rust-test-font-lock
|
||
"let x = &{1};"
|
||
'("let" font-lock-keyword-face
|
||
"x" font-lock-variable-name-face
|
||
"&" rust-ampersand-face))
|
||
(rust-test-font-lock
|
||
"let f = &|x| {x + 1};"
|
||
'("let" font-lock-keyword-face
|
||
"f" font-lock-variable-name-face
|
||
"&" rust-ampersand-face))
|
||
(rust-test-font-lock
|
||
"let x: &_ = &1;"
|
||
'("let" font-lock-keyword-face
|
||
"x" font-lock-variable-name-face
|
||
"&" rust-ampersand-face
|
||
"&" rust-ampersand-face))
|
||
(rust-test-font-lock
|
||
"&[1,2]"
|
||
'("&" rust-ampersand-face)))
|
||
|
||
(ert-deftest font-lock-if-let-binding ()
|
||
(rust-test-font-lock
|
||
"if let Some(var) = some_var { /* no-op */ }"
|
||
'("if" font-lock-keyword-face
|
||
"let" font-lock-keyword-face
|
||
"Some" font-lock-type-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face)))
|
||
|
||
(ert-deftest font-lock-single-quote-character-literal ()
|
||
(rust-test-font-lock
|
||
"fn main() { let ch = '\\''; }"
|
||
'("fn" font-lock-keyword-face
|
||
"main" font-lock-function-name-face
|
||
"let" font-lock-keyword-face
|
||
"ch" font-lock-variable-name-face
|
||
"'\\''" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-escaped-double-quote-character-literal ()
|
||
(rust-test-font-lock
|
||
"fn main() { let ch = '\\\"'; }"
|
||
'("fn" font-lock-keyword-face
|
||
"main" font-lock-function-name-face
|
||
"let" font-lock-keyword-face
|
||
"ch" font-lock-variable-name-face
|
||
"'\\\"'" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-escaped-backslash-character-literal ()
|
||
(rust-test-font-lock
|
||
"fn main() { let ch = '\\\\'; }"
|
||
'("fn" font-lock-keyword-face
|
||
"main" font-lock-function-name-face
|
||
"let" font-lock-keyword-face
|
||
"ch" font-lock-variable-name-face
|
||
"'\\\\'" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-hex-escape-character-literal ()
|
||
(rust-test-font-lock
|
||
"let ch = '\\x1f';"
|
||
'("let" font-lock-keyword-face
|
||
"ch" font-lock-variable-name-face
|
||
"'\\x1f'" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-unicode-escape-character-literal ()
|
||
(rust-test-font-lock
|
||
"let ch = '\\u{1ffff}';"
|
||
'("let" font-lock-keyword-face
|
||
"ch" font-lock-variable-name-face
|
||
"'\\u{1ffff}'" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-raw-strings-no-hashes ()
|
||
(rust-test-font-lock
|
||
"r\"No hashes\";"
|
||
'("r\"No hashes\"" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-raw-strings-double-quote ()
|
||
(rust-test-font-lock
|
||
"fn main() {
|
||
r#\"With a double quote (\")\"#;
|
||
}
|
||
"
|
||
'("fn" font-lock-keyword-face
|
||
"main" font-lock-function-name-face
|
||
"r#\"With a double quote (\")\"#" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-raw-strings-two-hashes ()
|
||
(rust-test-font-lock
|
||
"r##\"With two hashes\"##;"
|
||
'("r##\"With two hashes\"##" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-raw-strings-backslash-at-end ()
|
||
(rust-test-font-lock
|
||
"r\"With a backslash at the end\\\";"
|
||
'("r\"With a backslash at the end\\\"" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-two-raw-strings ()
|
||
(rust-test-font-lock
|
||
"fn main() {
|
||
r\"With a backslash at the end\\\";
|
||
r##\"With two hashes\"##;
|
||
}"
|
||
'("fn" font-lock-keyword-face
|
||
"main" font-lock-function-name-face
|
||
"r\"With a backslash at the end\\\"" font-lock-string-face
|
||
"r##\"With two hashes\"##" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-raw-string-with-inner-hash ()
|
||
(rust-test-font-lock
|
||
"r##\"I've got an octothorpe (#)\"##; foo()"
|
||
'("r##\"I've got an octothorpe (#)\"##" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-raw-string-with-inner-quote-and-hash ()
|
||
(rust-test-font-lock
|
||
"not_the_string(); r##\"string \"# still same string\"##; not_the_string()"
|
||
'("r##\"string \"# still same string\"##" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-string-ending-with-r-not-raw-string ()
|
||
(rust-test-font-lock
|
||
"fn f() {
|
||
\"Er\";
|
||
}
|
||
|
||
fn g() {
|
||
\"xs\";
|
||
}"
|
||
'("fn" font-lock-keyword-face
|
||
"f" font-lock-function-name-face
|
||
"\"Er\"" font-lock-string-face
|
||
"fn" font-lock-keyword-face
|
||
"g" font-lock-function-name-face
|
||
"\"xs\"" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-string-ending-with-r-word-boundary ()
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(insert "const foo = \"foo bar\"")
|
||
(font-lock-flush)
|
||
(font-lock-ensure)
|
||
;; right-word should move the point to the end of the words.
|
||
(goto-char 14)
|
||
(right-word)
|
||
(should (equal 17 (point)))
|
||
(right-word)
|
||
(should (equal 21 (point)))
|
||
))
|
||
|
||
(ert-deftest font-lock-raw-string-trick-ending-followed-by-string-with-quote ()
|
||
(rust-test-font-lock
|
||
"r\"With what looks like the start of a raw string at the end r#\";
|
||
not_a_string();
|
||
r##\"With \"embedded\" quote \"##;"
|
||
'("r\"With what looks like the start of a raw string at the end r#\"" font-lock-string-face
|
||
"r##\"With \"embedded\" quote \"##" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-raw-string-starter-inside-raw-string ()
|
||
;; Check that it won't look for a raw string beginning inside another raw string.
|
||
(rust-test-font-lock
|
||
"r#\"In the first string r\" in the first string \"#;
|
||
not_in_a_string();
|
||
r##\"In the second string\"##;"
|
||
'("r#\"In the first string r\" in the first string \"#" font-lock-string-face
|
||
"r##\"In the second string\"##" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-raw-string-starter-inside-comment ()
|
||
;; Check that it won't look for a raw string beginning inside another raw string.
|
||
(rust-test-font-lock
|
||
"// r\" this is a comment
|
||
\"this is a string\";
|
||
this_is_not_a_string();)"
|
||
'("// " font-lock-comment-delimiter-face
|
||
"r\" this is a comment\n" font-lock-comment-face
|
||
"\"this is a string\"" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-runaway-raw-string ()
|
||
(rust-test-font-lock
|
||
"const Z = r#\"my raw string\";\n// oops this is still in the string"
|
||
'("const" font-lock-keyword-face
|
||
"Z" font-lock-type-face
|
||
"r#\"my raw string\";\n// oops this is still in the string" font-lock-string-face))
|
||
)
|
||
|
||
(ert-deftest font-lock-recognize-closing-raw-string ()
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(insert "const foo = r##\"
|
||
1...............................................50
|
||
1...............................................50
|
||
1...............................................50
|
||
1...............195-->\"; let ...................50
|
||
1...............................................50
|
||
1...............................................50
|
||
1...............................................50
|
||
1...............................................50
|
||
1...............................................50
|
||
1......................500......................50
|
||
\"#;
|
||
")
|
||
(font-lock-flush)
|
||
(font-lock-ensure)
|
||
(goto-char 530)
|
||
(insert "#")
|
||
;; We have now closed the raw string. Check that the whole string is
|
||
;; recognized after the change
|
||
(font-lock-after-change-function (1- (point)) (point) 0)
|
||
(should (equal 'font-lock-string-face (get-text-property 195 'face))) ;; The "let"
|
||
(should (equal 'font-lock-string-face (get-text-property 500 'face))) ;; The "500"
|
||
(should (equal nil (get-text-property 531 'face))) ;; The second ";"
|
||
))
|
||
|
||
;;; Documentation comments
|
||
|
||
(ert-deftest font-lock-doc-line-comment-parent ()
|
||
(rust-test-font-lock
|
||
"//! doc"
|
||
'("//! doc" font-lock-doc-face)))
|
||
|
||
(ert-deftest font-lock-doc-line-comment-item ()
|
||
(rust-test-font-lock
|
||
"/// doc"
|
||
'("/// doc" font-lock-doc-face)))
|
||
|
||
(ert-deftest font-lock-nondoc-line ()
|
||
(rust-test-font-lock
|
||
"////// doc"
|
||
'("////// " font-lock-comment-delimiter-face
|
||
"doc" font-lock-comment-face)))
|
||
|
||
(ert-deftest font-lock-doc-line-in-string ()
|
||
(rust-test-font-lock
|
||
"\"/// doc\""
|
||
'("\"/// doc\"" font-lock-string-face))
|
||
|
||
(rust-test-font-lock
|
||
"\"//! doc\""
|
||
'("\"//! doc\"" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-doc-line-in-nested-comment ()
|
||
(rust-test-font-lock
|
||
"/* /// doc */"
|
||
'("/* " font-lock-comment-delimiter-face
|
||
"/// doc */" font-lock-comment-face))
|
||
|
||
(rust-test-font-lock
|
||
"/* //! doc */"
|
||
'("/* " font-lock-comment-delimiter-face
|
||
"//! doc */" font-lock-comment-face)))
|
||
|
||
|
||
(ert-deftest font-lock-doc-block-comment-parent ()
|
||
(rust-test-font-lock
|
||
"/*! doc */"
|
||
'("/*! doc */" font-lock-doc-face)))
|
||
|
||
(ert-deftest font-lock-doc-block-comment-item ()
|
||
(rust-test-font-lock
|
||
"/** doc */"
|
||
'("/** doc */" font-lock-doc-face)))
|
||
|
||
(ert-deftest font-lock-nondoc-block-comment-item ()
|
||
(rust-test-font-lock
|
||
"/***** doc */"
|
||
'("/**" font-lock-comment-delimiter-face
|
||
"*** doc */" font-lock-comment-face)))
|
||
|
||
(ert-deftest font-lock-doc-block-in-string ()
|
||
(rust-test-font-lock
|
||
"\"/** doc */\""
|
||
'("\"/** doc */\"" font-lock-string-face))
|
||
(rust-test-font-lock
|
||
"\"/*! doc */\""
|
||
'("\"/*! doc */\"" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-module-def ()
|
||
(rust-test-font-lock
|
||
"mod foo;"
|
||
'("mod" font-lock-keyword-face
|
||
"foo" font-lock-constant-face)))
|
||
|
||
(ert-deftest font-lock-module-use ()
|
||
(rust-test-font-lock
|
||
"use foo;"
|
||
'("use" font-lock-keyword-face
|
||
"foo" font-lock-constant-face)))
|
||
|
||
(ert-deftest font-lock-module-path ()
|
||
(rust-test-font-lock
|
||
"foo::bar"
|
||
'("foo" font-lock-constant-face)))
|
||
|
||
(ert-deftest font-lock-submodule-path ()
|
||
(rust-test-font-lock
|
||
"foo::bar::baz"
|
||
'("foo" font-lock-constant-face
|
||
"bar" font-lock-constant-face)))
|
||
|
||
(ert-deftest font-lock-type ()
|
||
(rust-test-font-lock
|
||
"foo::Bar::baz"
|
||
'("foo" font-lock-constant-face
|
||
"Bar" font-lock-type-face)))
|
||
|
||
(ert-deftest font-lock-type-annotation ()
|
||
"Ensure type annotations are not confused with modules."
|
||
(rust-test-font-lock
|
||
"parse::<i32>();"
|
||
;; Only the i32 should have been highlighted.
|
||
'("i32" font-lock-type-face))
|
||
(rust-test-font-lock
|
||
"foo:: <i32>"
|
||
;; Only the i32 should have been highlighted.
|
||
'("i32" font-lock-type-face)))
|
||
|
||
(ert-deftest font-lock-question-mark ()
|
||
"Ensure question mark operator is highlighted."
|
||
(rust-test-font-lock
|
||
"?"
|
||
'("?" rust-question-mark))
|
||
(rust-test-font-lock
|
||
"foo\(\)?;"
|
||
'("?" rust-question-mark))
|
||
(rust-test-font-lock
|
||
"foo\(bar\(\)?\);"
|
||
'("?" rust-question-mark))
|
||
(rust-test-font-lock
|
||
"\"?\""
|
||
'("\"?\"" font-lock-string-face))
|
||
(rust-test-font-lock
|
||
"foo\(\"?\"\);"
|
||
'("\"?\"" font-lock-string-face))
|
||
(rust-test-font-lock
|
||
"// ?"
|
||
'("// " font-lock-comment-delimiter-face
|
||
"?" font-lock-comment-face))
|
||
(rust-test-font-lock
|
||
"/// ?"
|
||
'("/// ?" font-lock-doc-face))
|
||
(rust-test-font-lock
|
||
"foo\(\"?\"\);"
|
||
'("\"?\"" font-lock-string-face))
|
||
(rust-test-font-lock
|
||
"foo\(\"?\"\)?;"
|
||
'("\"?\"" font-lock-string-face
|
||
"?" rust-question-mark)))
|
||
|
||
(ert-deftest rust-test-default-context-sensitive ()
|
||
(rust-test-font-lock
|
||
"let default = 7; impl foo { default fn f() { } }"
|
||
'("let" font-lock-keyword-face
|
||
"default" font-lock-variable-name-face
|
||
"impl" font-lock-keyword-face
|
||
"default" font-lock-keyword-face
|
||
"fn" font-lock-keyword-face
|
||
"f" font-lock-function-name-face)))
|
||
|
||
(ert-deftest rust-test-union-context-sensitive ()
|
||
(rust-test-font-lock
|
||
"let union = 7; union foo { x: &'union bar }"
|
||
'("let" font-lock-keyword-face
|
||
;; The first union is a variable name.
|
||
"union" font-lock-variable-name-face
|
||
;; The second union is a contextual keyword.
|
||
"union" font-lock-keyword-face
|
||
"foo" font-lock-type-face
|
||
"x" font-lock-variable-name-face
|
||
;; This union is the name of a lifetime.
|
||
"&" rust-ampersand-face
|
||
"union" font-lock-variable-name-face
|
||
"bar" font-lock-type-face)))
|
||
|
||
(ert-deftest indent-method-chains-no-align ()
|
||
(let ((rust-indent-method-chain nil)) (test-indent
|
||
"
|
||
fn main() {
|
||
let x = thing.do_it()
|
||
.aligned()
|
||
.more_alignment();
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-method-chains-no-align-with-question-mark-operator ()
|
||
(let ((rust-indent-method-chain nil)) (test-indent
|
||
"
|
||
fn main() {
|
||
let x = thing.do_it()
|
||
.aligned()
|
||
.more_alignment()?
|
||
.more_alignment();
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-method-chains-with-align ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn main() {
|
||
let x = thing.do_it()
|
||
.aligned()
|
||
.more_alignment();
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-method-chains-with-align-with-question-mark-operator ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn main() {
|
||
let x = thing.do_it()
|
||
.aligned()
|
||
.more_alignment()?
|
||
.more_alignment();
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-method-chains-with-align-and-second-stmt ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn main() {
|
||
let x = thing.do_it()
|
||
.aligned()
|
||
.more_alignment();
|
||
foo.bar();
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-method-chains-field ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn main() {
|
||
let x = thing.do_it
|
||
.aligned
|
||
.more_alignment();
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-method-chains-double-field-on-first-line ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn main() {
|
||
let x = thing.a.do_it
|
||
.aligned
|
||
.more_alignment();
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-method-chains-no-let ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn main() {
|
||
thing.a.do_it
|
||
.aligned
|
||
.more_alignment();
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-method-chains-look-over-comment ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn main() {
|
||
thing.a.do_it
|
||
// A comment
|
||
.aligned
|
||
// Another comment
|
||
.more_alignment();
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-method-chains-comment ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn main() {
|
||
// thing.do_it()
|
||
// .aligned()
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-method-chains-close-block ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn main() {
|
||
foo.bar()
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-method-chains-after-comment ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn main() { // comment here should not push next line out
|
||
foo.bar()
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-method-chains-after-comment2 ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn main() {
|
||
// Lorem ipsum lorem ipsum lorem ipsum lorem.ipsum
|
||
foo.bar()
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-function-after-where ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn each_split_within<'a, F>(ss: &'a str, lim: usize, mut it: F)
|
||
-> bool where F: FnMut(&'a str) -> bool {
|
||
}
|
||
|
||
#[test]
|
||
fn test_split_within() {
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest indent-function-after-where-nested ()
|
||
(let ((rust-indent-method-chain t)) (test-indent
|
||
"
|
||
fn outer() {
|
||
fn each_split_within<'a, F>(ss: &'a str, lim: usize, mut it: F)
|
||
-> bool where F: FnMut(&'a str) -> bool {
|
||
}
|
||
#[test]
|
||
fn test_split_within() {
|
||
}
|
||
fn bar() {
|
||
}
|
||
}
|
||
"
|
||
)))
|
||
|
||
(ert-deftest test-for-issue-36-syntax-corrupted-state ()
|
||
"This is a test for a issue #36, which involved emacs's
|
||
internal state getting corrupted when actions were done in a
|
||
specific sequence. The test seems arbitrary, and is, but it was
|
||
not clear how to narrow it down further.
|
||
|
||
The cause of the bug was code that used to set
|
||
`syntax-begin-function' to `beginning-of-defun', which doesn't
|
||
actually fulfill the expectations--`syntax-begin-function' is
|
||
supposed to back out of all parens, but `beginning-of-defun'
|
||
could leave it inside parens if a fn appears inside them.
|
||
|
||
Having said that, as I write this I don't understand fully what
|
||
internal state was corrupted and how. There wasn't an obvious
|
||
pattern to what did and did not trip it."
|
||
|
||
;; When bug #36 was present, the following test would pass, but running it
|
||
;; caused some unknown emacs state to be corrupted such that the following
|
||
;; test failed. Both the "blank_line" and "indented_closing_brace" functions
|
||
;; were needed to expose the error, for instance--deleting either of them
|
||
;; would make the failure go away.
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(insert "fn blank_line(arg:i32) -> bool {
|
||
|
||
}
|
||
|
||
fn indenting_closing_brace() {
|
||
if(true) {
|
||
}
|
||
}
|
||
|
||
fn indented_already() {
|
||
\n // The previous line already has its spaces
|
||
}
|
||
")
|
||
(font-lock-flush)
|
||
(font-lock-ensure)
|
||
(goto-char (point-min))
|
||
(forward-line 10)
|
||
(move-to-column 0)
|
||
(indent-for-tab-command)
|
||
(should (equal (current-column) 4))
|
||
)
|
||
|
||
;; This is the test that would fail only after running the previous one. The
|
||
;; code is extracted from src/libstd/collections/table.rs in the rust tree.
|
||
;; It was not clear how to reduce it further--removing various bits of it
|
||
;; would make it no longer fail. In particular, changing only the comment at
|
||
;; the top of the "next" function was sufficient to make it no longer fail.
|
||
(test-indent
|
||
"
|
||
impl Foo for Bar {
|
||
|
||
/// Modifies the bucket pointer in place to make it point to the next slot.
|
||
pub fn next(&mut self) {
|
||
// Branchless bucket
|
||
// As we reach the end of the table...
|
||
// We take the current idx: 0111111b
|
||
// Xor it by its increment: ^ 1000000b
|
||
// ------------
|
||
// 1111111b
|
||
// Then AND with the capacity: & 1000000b
|
||
// ------------
|
||
// to get the backwards offset: 1000000b
|
||
let maybe_wraparound_dist = (self.idx ^ (self.idx + 1)) & self.table.capacity();
|
||
// Finally, we obtain the offset 1 or the offset -cap + 1.
|
||
let dist = 1 - (maybe_wraparound_dist as isize);
|
||
|
||
self.idx += 1;
|
||
|
||
unsafe {
|
||
self.raw = self.raw.offset(dist);
|
||
}
|
||
}
|
||
|
||
/// Reads a bucket at a given index, returning an enum indicating whether
|
||
/// the appropriate types to call most of the other functions in
|
||
/// this module.
|
||
pub fn peek(self) {
|
||
match foo {
|
||
EMPTY_BUCKET =>
|
||
Empty(EmptyBucket {
|
||
raw: self.raw,
|
||
idx: self.idx,
|
||
table: self.table
|
||
}),
|
||
_ =>
|
||
Full(FullBucket {
|
||
raw: self.raw,
|
||
idx: self.idx,
|
||
table: self.table
|
||
})
|
||
}
|
||
}
|
||
}
|
||
"
|
||
))
|
||
|
||
(ert-deftest test-indent-string-with-eol-backslash ()
|
||
(test-indent
|
||
"
|
||
pub fn foo() {
|
||
format!(\"abc \\
|
||
def\")
|
||
}
|
||
"
|
||
))
|
||
|
||
(ert-deftest test-indent-string-with-eol-backslash-at-start ()
|
||
(test-indent
|
||
"
|
||
pub fn foo() {
|
||
format!(\"\\
|
||
abc \\
|
||
def\")
|
||
}
|
||
"
|
||
))
|
||
|
||
(ert-deftest test-indent-string-without-eol-backslash-indent-is-not-touched ()
|
||
(test-indent
|
||
"
|
||
pub fn foo() {
|
||
format!(\"
|
||
abc
|
||
def\");
|
||
}
|
||
|
||
pub fn foo() {
|
||
format!(\"la la la
|
||
la
|
||
la la\");
|
||
}
|
||
"
|
||
;; Should still indent the code parts but leave the string internals alone:
|
||
"
|
||
pub fn foo() {
|
||
format!(\"
|
||
abc
|
||
def\");
|
||
}
|
||
|
||
pub fn foo() {
|
||
format!(\"la la la
|
||
la
|
||
la la\");
|
||
}
|
||
"
|
||
))
|
||
|
||
(ert-deftest test-indent-string-eol-backslash-mixed-with-literal-eol ()
|
||
(test-indent
|
||
"
|
||
fn foo() {
|
||
println!(\"
|
||
Here is the beginning of the string
|
||
and here is a line that is arbitrarily indented \\
|
||
and a continuation of that indented line
|
||
and another arbitrary indentation
|
||
still another
|
||
yet another \\
|
||
with a line continuing it
|
||
And another line not indented
|
||
\")
|
||
}
|
||
"
|
||
"
|
||
fn foo() {
|
||
println!(\"
|
||
Here is the beginning of the string
|
||
and here is a line that is arbitrarily indented \\
|
||
and a continuation of that indented line
|
||
and another arbitrary indentation
|
||
still another
|
||
yet another \\
|
||
with a line continuing it
|
||
And another line not indented
|
||
\")
|
||
}
|
||
"))
|
||
|
||
(ert-deftest test-indent-string-eol-backslash-dont-touch-raw-strings ()
|
||
(test-indent
|
||
"
|
||
pub fn foo() {
|
||
format!(r\"\
|
||
abc\
|
||
def\");
|
||
}
|
||
|
||
pub fn foo() {
|
||
format!(r\"la la la
|
||
la\
|
||
la la\");
|
||
}
|
||
"
|
||
;; Should still indent the code parts but leave the string internals alone:
|
||
"
|
||
pub fn foo() {
|
||
format!(r\"\
|
||
abc\
|
||
def\");
|
||
}
|
||
|
||
pub fn foo() {
|
||
format!(r\"la la la
|
||
la\
|
||
la la\");
|
||
}
|
||
"
|
||
))
|
||
|
||
(ert-deftest indent-inside-string-first-line ()
|
||
(test-indent
|
||
;; Needs to leave 1 space before "world"
|
||
"\"hello \\\n world\""))
|
||
|
||
(ert-deftest indent-multi-line-type-param-list ()
|
||
(test-indent
|
||
"
|
||
pub fn foo<T,
|
||
V>() {
|
||
hello();
|
||
}"))
|
||
|
||
(ert-deftest indent-open-paren-in-column0 ()
|
||
;; Just pass the same text for the "deindented" argument. This
|
||
;; avoids the extra spaces normally inserted, which would mess up
|
||
;; the test because string contents aren't touched by reindentation.
|
||
(let ((text "
|
||
const a: &'static str = r#\"
|
||
{}\"#;
|
||
fn main() {
|
||
let b = \"//\";
|
||
let c = \"\";
|
||
|
||
}
|
||
"))
|
||
(test-indent text text)))
|
||
|
||
(ert-deftest indent-question-mark-operator ()
|
||
(test-indent "fn foo() {
|
||
if bar()? < 1 {
|
||
}
|
||
baz();
|
||
}"))
|
||
|
||
;; Regression test for #212.
|
||
(ert-deftest indent-left-shift ()
|
||
(test-indent "
|
||
fn main() {
|
||
let a = [[0u32, 0u32]; 1];
|
||
let i = 0;
|
||
let x = a[i][(1 < i)];
|
||
let x = a[i][(1 << i)];
|
||
}
|
||
"))
|
||
|
||
(defun rust-test-matching-parens (content pairs &optional nonparen-positions)
|
||
"Assert that in rust-mode, given a buffer with the given `content',
|
||
emacs's paren matching will find all of the pairs of positions
|
||
as matching braces. The list of nonparen-positions asserts
|
||
specific positions that should NOT be considered to be
|
||
parens/braces of any kind.
|
||
|
||
This does not assert that the `pairs' list is
|
||
comprehensive--there can be additional pairs that don't appear
|
||
in the list and the test still passes (as long as none of their
|
||
positions appear in `nonparen-positions'.)"
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(insert content)
|
||
(font-lock-flush)
|
||
(font-lock-ensure)
|
||
(dolist (pair pairs)
|
||
(let* ((open-pos (nth 0 pair))
|
||
(close-pos (nth 1 pair)))
|
||
(should (equal 4 (syntax-class (syntax-after open-pos))))
|
||
(should (equal 5 (syntax-class (syntax-after close-pos))))
|
||
(should (equal (scan-sexps open-pos 1) (+ 1 close-pos)))
|
||
(should (equal (scan-sexps (+ 1 close-pos) -1) open-pos))))
|
||
(dolist (nonpar-pos nonparen-positions)
|
||
(let ((nonpar-syntax-class (syntax-class (syntax-after nonpar-pos))))
|
||
(should-not (equal 4 nonpar-syntax-class))
|
||
(should-not (equal 5 nonpar-syntax-class))))))
|
||
|
||
(ert-deftest rust-test-unmatched-single-quote-in-comment-paren-matching ()
|
||
;; This was a bug from the char quote handling that affected the paren
|
||
;; matching. An unmatched quote char in a comment caused the problems.
|
||
(rust-test-matching-parens
|
||
"// If this appeared first in the file...
|
||
\"\\
|
||
{\";
|
||
|
||
// And the { was not the on the first column:
|
||
{
|
||
// This then messed up the paren matching: '\\'
|
||
}
|
||
|
||
"
|
||
'((97 150) ;; The { and } at the bottom
|
||
)))
|
||
|
||
(ert-deftest rust-test-two-character-quotes-in-a-row ()
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(font-lock-flush)
|
||
(font-lock-ensure)
|
||
(insert "'\\n','a', fn")
|
||
(font-lock-after-change-function 1 12 0)
|
||
|
||
(should (equal 'font-lock-string-face (get-text-property 3 'face)))
|
||
(should (equal nil (get-text-property 5 'face)))
|
||
(should (equal 'font-lock-string-face (get-text-property 7 'face)))
|
||
(should (equal nil (get-text-property 9 'face)))
|
||
(should (equal 'font-lock-keyword-face (get-text-property 12 'face)))
|
||
)
|
||
)
|
||
|
||
(ert-deftest single-quote-null-char ()
|
||
(rust-test-font-lock
|
||
"'\\0' 'a' fn"
|
||
'("'\\0'" font-lock-string-face
|
||
"'a'" font-lock-string-face
|
||
"fn" font-lock-keyword-face)))
|
||
|
||
(ert-deftest r-in-string-after-single-quoted-double-quote ()
|
||
(rust-test-font-lock
|
||
"'\"';\n\"r\";\n\"oops\";"
|
||
'("'\"'" font-lock-string-face
|
||
"\"r\"" font-lock-string-face
|
||
"\"oops\"" font-lock-string-face
|
||
)))
|
||
|
||
(ert-deftest char-literal-after-quote-in-raw-string ()
|
||
(rust-test-font-lock
|
||
"r#\"\"\"#;\n'q'"
|
||
'("r#\"\"\"#" font-lock-string-face
|
||
"'q'" font-lock-string-face)))
|
||
|
||
(ert-deftest rust-macro-font-lock ()
|
||
(rust-test-font-lock
|
||
"foo!\(\);"
|
||
'("foo!" font-lock-preprocessor-face))
|
||
(rust-test-font-lock
|
||
"foo!{};"
|
||
'("foo!" font-lock-preprocessor-face))
|
||
(rust-test-font-lock
|
||
"foo![];"
|
||
'("foo!" font-lock-preprocessor-face)))
|
||
|
||
(ert-deftest rust-string-interpolation-matcher-works ()
|
||
(dolist (test '(("print!\(\"\"\)" 9 11 nil)
|
||
("print!\(\"abcd\"\)" 9 15 nil)
|
||
("print!\(\"abcd {{}}\"\);" 9 19 nil)
|
||
("print!\(\"abcd {{\"\);" 9 18 nil)
|
||
("print!\(\"abcd {}\"\);" 9 18 ((14 16)))
|
||
("print!\(\"abcd {{{}\"\);" 9 20 ((16 18)))
|
||
("print!\(\"abcd {}{{\"\);" 9 20 ((14 16)))
|
||
("print!\(\"abcd {} {{\"\);" 9 21 ((14 16)))
|
||
("print!\(\"abcd {}}}\"\);" 9 20 ((14 16)))
|
||
("print!\(\"abcd {{{}}}\"\);" 9 20 ((16 18)))
|
||
("print!\(\"abcd {0}\"\);" 9 18 ((14 17)))
|
||
("print!\(\"abcd {0} efgh\"\);" 9 23 ((14 17)))
|
||
("print!\(\"{1} abcd {0} efgh\"\);" 9 27 ((9 12) (18 21)))
|
||
("print!\(\"{{{1} abcd }} {0}}} {{efgh}}\"\);" 9 33 ((11 14) (23 26)))))
|
||
(cl-destructuring-bind (text cursor limit matches) test
|
||
(with-temp-buffer
|
||
;; make sure we have a clean slate
|
||
(save-match-data
|
||
(set-match-data nil)
|
||
(insert text)
|
||
(goto-char cursor)
|
||
(if (null matches)
|
||
(should (equal (progn
|
||
(rust-string-interpolation-matcher limit)
|
||
(match-data))
|
||
nil))
|
||
(dolist (pair matches)
|
||
(rust-string-interpolation-matcher limit)
|
||
(should (equal (match-beginning 0) (car pair)))
|
||
(should (equal (match-end 0) (cadr pair))))))))))
|
||
|
||
(ert-deftest rust-formatting-macro-font-lock ()
|
||
;; test that the block delimiters aren't highlighted and the comment
|
||
;; is ignored
|
||
(rust-test-font-lock
|
||
"print!(\"\"); { /* print!(\"\"); */ }"
|
||
'("print!" rust-builtin-formatting-macro
|
||
"\"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"print!(\"\"); */" font-lock-comment-face))
|
||
;; with newline directly following delimiter
|
||
(rust-test-font-lock
|
||
"print!(\n\"\"\n); { /* print!(\"\"); */ }"
|
||
'("print!" rust-builtin-formatting-macro
|
||
"\"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"print!(\"\"); */" font-lock-comment-face))
|
||
;; with empty println!()
|
||
(rust-test-font-lock
|
||
"println!(); { /* println!(); */ }"
|
||
'("println!" rust-builtin-formatting-macro
|
||
"/* " font-lock-comment-delimiter-face
|
||
"println!(); */" font-lock-comment-face))
|
||
;; other delimiters
|
||
(rust-test-font-lock
|
||
"print!{\"\"}; { /* no-op */ }"
|
||
'("print!" rust-builtin-formatting-macro
|
||
"\"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; other delimiters
|
||
(rust-test-font-lock
|
||
"print![\"\"]; { /* no-op */ }"
|
||
'("print!" rust-builtin-formatting-macro
|
||
"\"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; no interpolation
|
||
(rust-test-font-lock
|
||
"print!(\"abcd\"); { /* no-op */ }"
|
||
'("print!" rust-builtin-formatting-macro
|
||
"\"abcd\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; only interpolation
|
||
(rust-test-font-lock
|
||
"print!(\"{}\"); { /* no-op */ }"
|
||
'("print!" rust-builtin-formatting-macro
|
||
"\"" font-lock-string-face
|
||
"{}" rust-string-interpolation
|
||
"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; text + interpolation
|
||
(rust-test-font-lock
|
||
"print!(\"abcd {}\", foo); { /* no-op */ }"
|
||
'("print!" rust-builtin-formatting-macro
|
||
"\"abcd " font-lock-string-face
|
||
"{}" rust-string-interpolation
|
||
"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; text + interpolation with specification
|
||
(rust-test-font-lock
|
||
"print!(\"abcd {0}\", foo); { /* no-op */ }"
|
||
'("print!" rust-builtin-formatting-macro
|
||
"\"abcd " font-lock-string-face
|
||
"{0}" rust-string-interpolation
|
||
"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; text + interpolation with specification and escape
|
||
(rust-test-font-lock
|
||
"print!(\"abcd {0}}}\", foo); { /* no-op */ }"
|
||
'("print!" rust-builtin-formatting-macro
|
||
"\"abcd " font-lock-string-face
|
||
"{0}" rust-string-interpolation
|
||
"}}\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; multiple pairs
|
||
(rust-test-font-lock
|
||
"print!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }"
|
||
'("print!" rust-builtin-formatting-macro
|
||
"\"abcd " font-lock-string-face
|
||
"{0}" rust-string-interpolation
|
||
" efgh " font-lock-string-face
|
||
"{1}" rust-string-interpolation
|
||
"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; println
|
||
(rust-test-font-lock
|
||
"println!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }"
|
||
'("println!" rust-builtin-formatting-macro
|
||
"\"abcd " font-lock-string-face
|
||
"{0}" rust-string-interpolation
|
||
" efgh " font-lock-string-face
|
||
"{1}" rust-string-interpolation
|
||
"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; eprint
|
||
(rust-test-font-lock
|
||
"eprint!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }"
|
||
'("eprint!" rust-builtin-formatting-macro
|
||
"\"abcd " font-lock-string-face
|
||
"{0}" rust-string-interpolation
|
||
" efgh " font-lock-string-face
|
||
"{1}" rust-string-interpolation
|
||
"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; eprintln
|
||
(rust-test-font-lock
|
||
"eprintln!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }"
|
||
'("eprintln!" rust-builtin-formatting-macro
|
||
"\"abcd " font-lock-string-face
|
||
"{0}" rust-string-interpolation
|
||
" efgh " font-lock-string-face
|
||
"{1}" rust-string-interpolation
|
||
"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; format
|
||
(rust-test-font-lock
|
||
"format!(\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }"
|
||
'("format!" rust-builtin-formatting-macro
|
||
"\"abcd " font-lock-string-face
|
||
"{0}" rust-string-interpolation
|
||
" efgh " font-lock-string-face
|
||
"{1}" rust-string-interpolation
|
||
"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; print + raw string
|
||
(rust-test-font-lock
|
||
"format!(r\"abcd {0} efgh {1}\", foo, bar); { /* no-op */ }"
|
||
'("format!" rust-builtin-formatting-macro
|
||
"r\"abcd " font-lock-string-face
|
||
"{0}" rust-string-interpolation
|
||
" efgh " font-lock-string-face
|
||
"{1}" rust-string-interpolation
|
||
"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; print + raw string with hash
|
||
(rust-test-font-lock
|
||
"format!(r#\"abcd {0} efgh {1}\"#, foo, bar); { /* no-op */ }"
|
||
'("format!" rust-builtin-formatting-macro
|
||
"r#\"abcd " font-lock-string-face
|
||
"{0}" rust-string-interpolation
|
||
" efgh " font-lock-string-face
|
||
"{1}" rust-string-interpolation
|
||
"\"#" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
;; print + raw string with two hashes
|
||
(rust-test-font-lock
|
||
"format!(r##\"abcd {0} efgh {1}\"##, foo, bar); { /* no-op */ }"
|
||
'("format!" rust-builtin-formatting-macro
|
||
"r##\"abcd " font-lock-string-face
|
||
"{0}" rust-string-interpolation
|
||
" efgh " font-lock-string-face
|
||
"{1}" rust-string-interpolation
|
||
"\"##" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face)))
|
||
|
||
(ert-deftest rust-write-macro-font-lock ()
|
||
(rust-test-font-lock
|
||
"write!(f, \"abcd {0}}} efgh {1}\", foo, bar); { /* no-op */ }"
|
||
'("write!" rust-builtin-formatting-macro
|
||
"\"abcd " font-lock-string-face
|
||
"{0}" rust-string-interpolation
|
||
"}} efgh " font-lock-string-face
|
||
"{1}" rust-string-interpolation
|
||
"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
(rust-test-font-lock
|
||
"writeln!(f, \"abcd {0}}} efgh {1}\", foo, bar); { /* no-op */ }"
|
||
'("writeln!" rust-builtin-formatting-macro
|
||
"\"abcd " font-lock-string-face
|
||
"{0}" rust-string-interpolation
|
||
"}} efgh " font-lock-string-face
|
||
"{1}" rust-string-interpolation
|
||
"\"" font-lock-string-face
|
||
"/* " font-lock-comment-delimiter-face
|
||
"no-op */" font-lock-comment-face))
|
||
(rust-test-font-lock
|
||
"println!(\"123\"); eprintln!(\"123\"); cprintln!(\"123\");"
|
||
'("println!" rust-builtin-formatting-macro
|
||
"\"123\"" font-lock-string-face
|
||
"eprintln!" rust-builtin-formatting-macro
|
||
"\"123\"" font-lock-string-face
|
||
"cprintln!" font-lock-preprocessor-face
|
||
"\"123\"" font-lock-string-face)))
|
||
|
||
(ert-deftest font-lock-fontify-angle-brackets ()
|
||
"Test that angle bracket fontify"
|
||
(should (equal (rust-test-fontify-string "<>") "<>"))
|
||
(should (equal (rust-test-fontify-string "<foo>") "<foo>"))
|
||
(should (equal (rust-test-fontify-string "<<>>") "<<>>"))
|
||
(should (equal (rust-test-fontify-string "<>>") "<>>"))
|
||
(should (equal (rust-test-fontify-string "<<>") "<<>")))
|
||
|
||
(ert-deftest rust-test-basic-paren-matching ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo() {
|
||
let a = [1, 2, 3];
|
||
}"
|
||
'((8 9) ;; Parens of foo()
|
||
(11 36) ;; Curly braces
|
||
(25 33) ;; Square brackets
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-generic-fn ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo<A>() {
|
||
}"
|
||
'((8 10) ;; Angle brackets <A>
|
||
(11 12) ;; Parens
|
||
(14 16) ;; Curly braces
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-generic-fn-with-return-value ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo<A>() -> bool {
|
||
false
|
||
}"
|
||
'((8 10) ;; Angle brackets <A>
|
||
(11 12) ;; Parens
|
||
(22 34) ;; Curly braces
|
||
)
|
||
|
||
'(15 ;; The ">" in "->" is not an angle bracket
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-match-stmt ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo() {
|
||
something_str(match <Type as Trait>::method() {
|
||
Some(_) => \"Got some\",
|
||
None => \"Nada\"
|
||
});
|
||
}"
|
||
'((8 9) ;; parens of fn foo
|
||
(11 127) ;; curly braces of foo
|
||
(30 124) ;; parens of something_str
|
||
(37 51) ;; angle brackets of <Type as Trait>
|
||
(60 61) ;; parens of method()
|
||
(63 123) ;; curly braces of match
|
||
(77 79) ;; parens of Some(_)
|
||
)
|
||
|
||
'(82 ;; > in first =>
|
||
112 ;; > in second =>
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-bitshift-operators ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo(z:i32) {
|
||
let a:Option<Result<i32,i32>> = Some(Ok(4 >> 1));
|
||
let b = a.map(|x| x.map(|y| y << 3));
|
||
let trick_question = z<<<Type as Trait>::method(); // First two <s are not brackets, third is
|
||
}"
|
||
'((34 50) ;; angle brackets of Option
|
||
(41 49) ;; angle brackets of Result
|
||
(142 156) ;; angle brackets of <Type as Trait>
|
||
)
|
||
'(64 ;; The >> inside Some(Ok()) are not angle brackets
|
||
65 ;; The >> inside Some(Ok()) are not angle brackets
|
||
106 ;; The << inside map() are not angle brackets
|
||
107 ;; The << inside map() are not angle brackets
|
||
140 ;; The << before <Type as Trait> are not angle brackets
|
||
141 ;; The << before <Type as Trait> are not angle brackets
|
||
183 ;; The < inside the comment
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-angle-bracket-after-colon-ident ()
|
||
(rust-test-matching-parens
|
||
"
|
||
struct Bla<T> {
|
||
a:Option<(i32,Option<bool>)>,
|
||
b:Option<T>,
|
||
c:bool
|
||
}
|
||
|
||
fn f(x:i32,y:Option<i32>) {
|
||
let z:Option<i32> = None;
|
||
let b:Bla<i8> = Bla{
|
||
a:None,
|
||
b:None,
|
||
c:x<y.unwrap();
|
||
}
|
||
}"
|
||
'((12 14) ;; Angle brackets of Bla<T>
|
||
(30 49) ;; Outer angle brackets of a:Option<...>
|
||
(42 47) ;; Inner angle brackets of Option<bool>
|
||
(64 66) ;; Angle brackets of Option<T>
|
||
(102 106) ;; Angle brackets of y:Option<i32>
|
||
(127 131) ;; Angle brackets of z:Option<i32>
|
||
(154 157) ;; Angle brackets of b:Bla<i8>
|
||
)
|
||
'(209 ;; less than operator in c:x<y.unwrap...
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-struct-literals ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo(x:i32) -> Bar {
|
||
Bar {
|
||
b:x<3
|
||
}
|
||
}"
|
||
'()
|
||
'(17 ;; the -> is not a brace
|
||
46 ;; x<3 the < is a less than sign
|
||
))
|
||
)
|
||
|
||
(ert-deftest rust-test-paren-matching-nested-struct-literals ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn f(x:i32,y:i32) -> Foo<Bar> {
|
||
Foo{
|
||
bar:Bar{
|
||
a:3,
|
||
b:x<y
|
||
}
|
||
}
|
||
}
|
||
"
|
||
'((26 30)) ;; Angle brackets of Foo<Bar>
|
||
)
|
||
'(92 ;; less than operator x<y
|
||
))
|
||
|
||
(ert-deftest rust-test-paren-matching-fn-types-in-type-params ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo<T:Fn() -> X<Y>>() -> Z {
|
||
}
|
||
"
|
||
'((8 23) ;; The angle brackets of foo<T...>
|
||
(20 22 ;; The angle brackets of X<Y>
|
||
))
|
||
'(17 ;; The first ->
|
||
28 ;; The second ->
|
||
)
|
||
))
|
||
|
||
(ert-deftest rust-test-paren-matching-lt-ops-in-fn-params-1 ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo(x:i32) {
|
||
f(x < 3);
|
||
}
|
||
"
|
||
'()
|
||
'(26 ;; The < inside f is a less than operator
|
||
)
|
||
))
|
||
|
||
(ert-deftest rust-test-paren-matching-lt-ops-in-fn-params-2 ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo(x:i32) -> bool {
|
||
return x < 3;
|
||
}
|
||
"
|
||
'()
|
||
'(17 ;; The ->
|
||
39 ;; The < after return is a less than operator
|
||
)
|
||
))
|
||
|
||
(ert-deftest rust-test-type-paren-matching-angle-brackets-in-type-items ()
|
||
(rust-test-matching-parens
|
||
"
|
||
type Foo = Blah<Z,Y>;
|
||
type Bar<X> = (Foo, Bletch<X>);
|
||
type ThisThing<Z,A,D,F> = HereYouGo<Z,Y<Fn(A) -> B<C<D>>,E<F>>>;"
|
||
'((17 21) ;; Angle brackets of Blah<Z,Y>
|
||
(32 34) ;; Angle brackets of Bar<X>
|
||
(50 52) ;; Angle brackets of Bletch<X>
|
||
(70 78) ;; Angle brackets of ThisThing<Z,A,D,F>
|
||
(91 118) ;; Angle brackets of HereYouGo<...>
|
||
(95 117) ;; Angle brackets of Y<Fn...>
|
||
(106 111) ;; Angle brackets of B<C<D>>
|
||
(108 110) ;; Angle brackets of C<D>
|
||
(114 116) ;; Angle brackets of E<F>
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-tuple-like-struct ()
|
||
(rust-test-matching-parens
|
||
"
|
||
struct A(Option<B>);
|
||
struct C<Q>(Result<Q,i32>);"
|
||
'((17 19) ;; The angle brackets <B>
|
||
(10 20) ;; The parens of A();
|
||
(31 33) ;; The angle brackets of C<Q>
|
||
(41 47) ;; The angle brackets of Result<Q,i32>
|
||
)
|
||
'()))
|
||
|
||
(ert-deftest rust-test-paren-matching-in-enum ()
|
||
(rust-test-matching-parens
|
||
"
|
||
enum Boo<A> {
|
||
TupleLike(Option<A>),
|
||
StructLike{foo: Result<A,i32>}
|
||
}"
|
||
'((10 12) ;; Angle brackets of Boo<A>
|
||
(36 38) ;; Angle brackets of Option<A>
|
||
(68 74) ;; Angle brackets of Result<A,i32>
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-assoc-type-bounds ()
|
||
(rust-test-matching-parens
|
||
"impl <A:B<AssocType = C<A> >> Thing<A> {}"
|
||
'((6 29) ;; Outer angle brackets of impl
|
||
(10 28) ;; Outer angle brackets of B<AssocType = C<A>>
|
||
(24 26) ;; Inner angle brackets of C<A>
|
||
(36 38) ;; Angle brackets of Thing<A>
|
||
)
|
||
))
|
||
|
||
(ert-deftest rust-test-paren-matching-plus-signs-in-expressions-and-bounds ()
|
||
;; Note that as I write this, the function "bluh" below does not compile, but
|
||
;; it warns that the equality constraint in a where clause is "not yet
|
||
;; supported." It seems that the compiler will support this eventually, so
|
||
;; the emacs mode needs to support it.
|
||
(rust-test-matching-parens
|
||
"fn foo<A:Trait1+Trait2<i32>,B>(a:A,b:B) -> bool where B:Trait3<Foo>+Trait4<Bar> {
|
||
2 + a < 3 && 3 + b > 11
|
||
}
|
||
|
||
fn bluh<A>() where A:Fn()+MyTrait<i32>, MyTrait<A>::AssocType = Option<bool> {
|
||
}
|
||
|
||
fn fluh<C>() where C:Fn(i32) -> (i32, i32) + SomeTrait<i32>, C::AssocType = OtherThing<bool> {
|
||
}"
|
||
'((7 30) ;; Angle brackets of foo<...>
|
||
(23 27) ;; Angle brackets of Trait2<i32>
|
||
(63 67) ;; Angle brackets of Trait3<Foo>
|
||
(75 79) ;; Angle brackets of Trait4<Bar>
|
||
|
||
(121 123) ;; Angle brackets of bluh<A>
|
||
(147 151) ;; Angle brackets of MyTrait<i32>
|
||
|
||
(161 163) ;; Angle brackets of MyTrait<A>
|
||
(184 189) ;; Angle brackets of Option<bool>
|
||
|
||
(203 205) ;; Angle brackets of <C>
|
||
(250 254) ;; Angle brackets of SomeTrait<i32>
|
||
(282 287) ;; Angle brackets of Option<bool>
|
||
)
|
||
'(93 ;; Less-than sign of a < 3
|
||
106 ;; Greater than sign of b > 11
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-generic-type-in-tuple-return-type ()
|
||
(rust-test-matching-parens
|
||
"pub fn take(mut self) -> (EmptyBucket<K, V, M>, K, V) {}"
|
||
'((38 46))
|
||
))
|
||
|
||
(ert-deftest rust-test-paren-matching-references-and-logical-and ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn ampersand_check(a: &Option<i32>, b:bool) -> &Option<u32> {
|
||
a.map(|x| {
|
||
b && x < 32
|
||
})
|
||
}"
|
||
'((31 35) ;; Option<i32>
|
||
(56 60) ;; Option<u32>
|
||
)
|
||
'(95 ;; x < 32
|
||
)
|
||
)
|
||
)
|
||
|
||
(ert-deftest rust-test-paren-matching-lt-sign-in-if-statement ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn if_check(a:i32,b:i32,c:i32) {
|
||
if a + b < c {
|
||
|
||
}
|
||
if a < b {
|
||
|
||
}
|
||
if (c < a) {
|
||
|
||
}
|
||
}
|
||
|
||
fn while_check(x:i32,y:i32) -> bool {
|
||
while x < y {
|
||
}
|
||
for x in y < x {
|
||
}
|
||
match y < z {
|
||
true => (), _ => ()
|
||
}
|
||
return z < y;
|
||
}"
|
||
'()
|
||
'(48 ;; b < c
|
||
78 ;; a < b
|
||
109 ;; (c < a)
|
||
|
||
184 ;; x < y
|
||
211 ;; y < x
|
||
235 ;; y < z
|
||
288 ;; z < y
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-lt-expr-with-field ()
|
||
(rust-test-matching-parens
|
||
"fn foo() { x.y < 3 }"
|
||
'()
|
||
'(16 ;; x.y < 3
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-lt-expr-with-quote ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn quote_check() {
|
||
'x' < y;
|
||
\"y\" < x;
|
||
r##\"z\"## < q;
|
||
a <= 3 && b < '2'
|
||
}"
|
||
'()
|
||
'(29 ;; 'x' < y
|
||
42 ;; "y" < x
|
||
60 ;; r##"z"## < q
|
||
71 ;; a <= '3'
|
||
81 ;; b < '2'
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-keywords-capitalized-are-ok-type-names ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo() -> Box<i32> {
|
||
let z:If<bool> = If(a < 3);
|
||
}"
|
||
'((17 21) ;; Box<i32>
|
||
(37 42) ;; If<bool>
|
||
)
|
||
'(51 ;; If(a < 3)
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-lt-expression-inside-macro ()
|
||
(rust-test-matching-parens
|
||
"fn bla() { assert!(x < y); }"
|
||
'()
|
||
'(22 ;; x < y
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-array-types-with-generics ()
|
||
(rust-test-matching-parens
|
||
"fn boo () -> [Option<i32>] {}"
|
||
'((21 25))))
|
||
|
||
(ert-deftest rust-test-paren-matching-angle-bracket-inner-reference ()
|
||
(rust-test-matching-parens
|
||
"fn x() -> Option<&Node<T>> {}"
|
||
'((17 26) ;; Option
|
||
(23 25) ;; Node
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-lt-operator-after-semicolon ()
|
||
(rust-test-matching-parens
|
||
"fn f(x:i32) -> bool { (); x < 3 }"
|
||
'()
|
||
'(29
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-lt-operator-after-comma ()
|
||
(rust-test-matching-parens
|
||
"fn foo() {
|
||
(e, a < b)
|
||
}"
|
||
'((16 25) ;; The parens ()
|
||
)
|
||
'(22 ;; The < operator
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-lt-operator-after-let ()
|
||
(rust-test-matching-parens
|
||
"fn main() {
|
||
let x = a < b;
|
||
}"
|
||
'((11 32) ;; The { }
|
||
)
|
||
'(27 ;; The < operator
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-two-lt-ops-in-a-row ()
|
||
(rust-test-matching-parens
|
||
"fn next(&mut self) -> Option<<I as Iterator>::Item>"
|
||
'((29 51) ;; Outer Option<>
|
||
(30 44) ;; Inner <I as Iterator>
|
||
)
|
||
'(21
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-lt-after-caret ()
|
||
(rust-test-matching-parens
|
||
"fn foo() { x^2 < 3 }"
|
||
'((10 20) ;; The { }
|
||
)
|
||
'(16 ;; The < operator
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-lt-operator-after-special-type ()
|
||
(rust-test-matching-parens
|
||
"fn foo() { low as u128 <= c }"
|
||
'((10 29))
|
||
'(24)))
|
||
|
||
(ert-deftest rust-test-paren-matching-lt-operator-after-closing-curly-brace ()
|
||
(rust-test-matching-parens
|
||
"fn main() { if true {} a < 3 }"
|
||
'((11 30)
|
||
)
|
||
'(26)))
|
||
|
||
(ert-deftest rust-test-paren-matching-const ()
|
||
(rust-test-matching-parens
|
||
"
|
||
const BLA = 1 << 3;
|
||
const BLUB = 2 < 4;"
|
||
'()
|
||
'(16
|
||
17 ;; Both chars of the << in 1 << 3
|
||
37 ;; The < in 2 < 4
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-c-like-enum ()
|
||
(rust-test-matching-parens
|
||
"
|
||
enum CLikeEnum {
|
||
Two = 1 << 1,
|
||
Four = 1 << 2
|
||
}"
|
||
'((17 56 ;; The { } of the enum
|
||
))
|
||
'(31
|
||
32 ;; The first <<
|
||
50
|
||
51 ;; The second <<
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-no-angle-brackets-in-macros ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo<A>(a:A) {
|
||
macro_a!( foo::<ignore the bracets> );
|
||
macro_b![ foo as Option<B> ];
|
||
}
|
||
|
||
macro_c!{
|
||
struct Boo<D> {}
|
||
}"
|
||
'((8 10))
|
||
;; Inside macros, it should not find any angle brackets, even if it normally
|
||
;; would
|
||
'(38 ;; macro_a <
|
||
57 ;; macro_a >
|
||
89 ;; macro_b <
|
||
91 ;; macro_b >
|
||
123 ;; macro_c <
|
||
125 ;; macro_d >
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-no-angle-brackets-in-macro-rules ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo<A>(a:A) {
|
||
macro_rules! foo ( foo::<ignore the bracets> );
|
||
macro_rules! bar [ foo as Option<B> ];
|
||
}
|
||
|
||
macro_c!{
|
||
struct Boo<D> {}
|
||
}"
|
||
'((8 10))
|
||
;; Inside macros, it should not find any angle brackets, even if it normally
|
||
;; would
|
||
'(47 ;; foo <
|
||
62 ;; foo >
|
||
107 ;; bar <
|
||
109 ;; bar >
|
||
141 ;; macro_c <
|
||
143 ;; macro_c >
|
||
)))
|
||
|
||
(ert-deftest rust-test-in-macro-do-not-fail-on-unbalance ()
|
||
(should
|
||
;; We don't care about the results here, so long as they do not error
|
||
(with-temp-buffer
|
||
(insert
|
||
"fn foo<A>(a:A) {
|
||
macro_c!{
|
||
struct Boo<D> {}
|
||
")
|
||
(rust-mode)
|
||
(goto-char (point-max))
|
||
(syntax-ppss))))
|
||
|
||
|
||
(ert-deftest rust-test-in-macro-around-opening ()
|
||
(should-not
|
||
(with-temp-buffer
|
||
(insert
|
||
"fn foo<A>(a:A) {
|
||
macro_c!{
|
||
struct Boo<D> {}
|
||
")
|
||
(rust-mode)
|
||
(search-backward "macro_c")
|
||
(and
|
||
(not (rust-in-macro))
|
||
(progn (forward-thing 'symbol 1) (not (rust-in-macro)))
|
||
(progn (forward-char 1) (rust-in-macro))
|
||
(progn (goto-char (point-max)) (rust-in-macro))))))
|
||
|
||
(ert-deftest rust-test-in-macro-nested ()
|
||
(with-temp-buffer
|
||
(insert
|
||
"macro_rules! outer {
|
||
() => { vec![] };
|
||
}")
|
||
(rust-mode)
|
||
(should (progn (goto-char 20) (not (rust-in-macro))))
|
||
(should (progn (goto-char 21) (eq (rust-in-macro) 20)))
|
||
(should (progn (goto-char 38) (eq (rust-in-macro) 20)))
|
||
(should (progn (goto-char 39) (eq (rust-in-macro) 38)))
|
||
(should (progn (goto-char 40) (eq (rust-in-macro) 20)))
|
||
(should (progn (goto-char 44) (eq (rust-in-macro) 20)))
|
||
(should (progn (goto-char 45) (not (rust-in-macro))))))
|
||
|
||
(ert-deftest rust-test-in-macro-not-with-space ()
|
||
(with-temp-buffer
|
||
(insert
|
||
"fn foo<T>() {
|
||
if !(mem::size_of::<T>() > 8) {
|
||
bar()
|
||
}
|
||
}")
|
||
(rust-mode)
|
||
(should (progn (goto-char 24) (not (rust-in-macro))))))
|
||
|
||
(ert-deftest rust-test-paren-matching-type-with-module-name ()
|
||
(rust-test-matching-parens
|
||
"
|
||
const X: libc::c_int = 1 << 2;
|
||
fn main() {
|
||
let z: libc::c_uint = 1 << 4;
|
||
}
|
||
"
|
||
'((43 79)) ;; The curly braces
|
||
'(27
|
||
28 ;; The first <<
|
||
73
|
||
74 ;; The second <<
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-qualififed-struct-literal ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn foo() -> Fn(asd) -> F<V> {
|
||
let z = foo::Struct{ b: 1 << 4, c: 2 < 4 }
|
||
}"
|
||
'((30 80) ;; Outer curly brackets
|
||
)
|
||
'(62
|
||
63 ;; The shift operator
|
||
73 ;; The less than operator
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-let-mut ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn f() {
|
||
let mut b = 1 < 3;
|
||
let mut i = 1 << 3;
|
||
}
|
||
"
|
||
'()
|
||
'(28 ;; 1 < 3
|
||
51
|
||
52 ;; 1 << 3
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-as-ref-type ()
|
||
(rust-test-matching-parens
|
||
"fn f() {
|
||
let a = b as &Foo<Bar>;
|
||
}"
|
||
'((31 35) ;; Angle brackets Foo<Bar>
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-type-ascription ()
|
||
(rust-test-matching-parens
|
||
"
|
||
fn rfc803() {
|
||
let z = a < b:FunnkyThing<i32>;
|
||
let s = Foo {
|
||
a: b < 3,
|
||
b: d:CrazyStuff<i32> < 3,
|
||
c: 2 < x:CrazyStuff<u128>
|
||
}
|
||
}"
|
||
'((45 49) ;; FunkyThing<i32>
|
||
(111 115) ;; CrazyStuff<i32>
|
||
(149 154) ;; CrazyStuff<u128>
|
||
)
|
||
'(30 ;; a < b
|
||
83 ;; b < 3
|
||
117 ;; d... < 3
|
||
135 ;; 2 < x
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-angle-brackets-in-enum-with-where-claause ()
|
||
(rust-test-matching-parens
|
||
"
|
||
enum MyEnum<T> where T:std::fmt::Debug {
|
||
Thing(Option<T>)
|
||
}"
|
||
'((13 15) ;; MyEnum<T>
|
||
(59 61) ;; Option<T>
|
||
)))
|
||
|
||
(ert-deftest rust-test-paren-matching-where-clauses-with-closure-types ()
|
||
(rust-test-matching-parens
|
||
"
|
||
enum Boo<'a,T> where T:Fn() -> Option<&'a str> + 'a {
|
||
Thingy(Option<&'a T>)
|
||
}
|
||
|
||
fn foo<'a>() -> Result<B,C> where C::X: D<A>, B:FnMut() -> Option<Q>+'a {
|
||
Foo(a < b)
|
||
}
|
||
|
||
type Foo<T> where T: Copy = Box<T>;
|
||
"
|
||
'((10 15) ;; Boo<'a,T>
|
||
(39 47) ;; Option<&'a str>
|
||
(72 78) ;; Option<&'a T>
|
||
|
||
(106 110) ;; Result<B,C>
|
||
(125 127) ;; D<A>
|
||
(149 151) ;; Option<Q>
|
||
(184 186) ;; Foo<T>
|
||
(207 209) ;; Box<T>
|
||
)
|
||
|
||
'(168 ;; Foo(a < b)
|
||
)
|
||
))
|
||
|
||
(ert-deftest rust-test-angle-bracket-matching-turned-off ()
|
||
(let ((rust-match-angle-brackets nil))
|
||
(rust-test-matching-parens
|
||
"fn foo<a>() {}"
|
||
'((10 11))
|
||
'(7 9))))
|
||
|
||
|
||
(ert-deftest redo-syntax-after-change-far-from-point ()
|
||
(let*
|
||
((tmp-file-name (make-temp-file "rust-mdoe-test-issue104"))
|
||
(base-contents (apply #'concat (append '("fn foo() {\n\n}\n") (make-list 500 "// More stuff...\n") '("fn bar() {\n\n}\n")))))
|
||
;; Create the temp file...
|
||
(with-temp-file tmp-file-name
|
||
(insert base-contents))
|
||
(with-temp-buffer
|
||
(insert-file-contents tmp-file-name 'VISIT nil nil 'REPLACE)
|
||
(rust-mode)
|
||
(goto-char (point-max))
|
||
(should (= 0 (rust-paren-level)))
|
||
(with-temp-file tmp-file-name
|
||
(insert base-contents)
|
||
(goto-char 12) ;; On the blank line in the middle of fn foo
|
||
(insert " let z = 1 < 3;")
|
||
)
|
||
(revert-buffer 'IGNORE-AUTO 'NOCONFIRM 'PRESERVE-MODES)
|
||
(should (= 0 (rust-paren-level)))
|
||
)
|
||
)
|
||
)
|
||
|
||
(defun test-imenu (code expected-items)
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(insert code)
|
||
(let ((actual-items
|
||
;; Replace ("item" . #<marker at ? in ?.rs) with "item"
|
||
(mapcar (lambda (class)
|
||
(cons (car class)
|
||
(mapcar #'car (cdr class))))
|
||
(imenu--generic-function rust-imenu-generic-expression))))
|
||
(should (equal expected-items actual-items)))))
|
||
|
||
(ert-deftest rust-test-imenu-extern-unsafe-fn ()
|
||
(test-imenu
|
||
"
|
||
fn f1() {
|
||
}
|
||
|
||
unsafe fn f2() {
|
||
}
|
||
|
||
extern \"C\" fn f3() {
|
||
}
|
||
|
||
pub extern fn f4() {
|
||
}
|
||
|
||
extern \"rust-intrinsic\" fn f5() {
|
||
}
|
||
|
||
async fn f6() {
|
||
}
|
||
|
||
const fn f7() {
|
||
}
|
||
|
||
async const fn not_a_match() {
|
||
}
|
||
|
||
fn f8<'a>() {
|
||
}
|
||
|
||
pub ( in self::super ) fn f9() {
|
||
}
|
||
|
||
pub ( in super ) fn f10() {
|
||
}
|
||
|
||
pub(in crate) fn f11() {
|
||
}
|
||
|
||
pub (in self) fn f12() {
|
||
}
|
||
"
|
||
'(("Fn"
|
||
"f1"
|
||
"f2"
|
||
"f3"
|
||
"f4"
|
||
"f5"
|
||
"f6"
|
||
"f7"
|
||
"f8"
|
||
"f9"
|
||
"f10"
|
||
"f11"
|
||
"f12"))))
|
||
|
||
(ert-deftest rust-test-imenu-impl-with-lifetime ()
|
||
(test-imenu
|
||
"
|
||
impl<'a> One<'a> {
|
||
fn one() {}
|
||
}
|
||
|
||
impl Two<'a> {
|
||
fn two() {}
|
||
}
|
||
"
|
||
'(("Impl" "One" "Two")
|
||
("Fn" "one" "two"))))
|
||
|
||
(ert-deftest font-lock-function-parameters ()
|
||
(rust-test-font-lock
|
||
"fn foo(a: u32, b : u32) {}"
|
||
'("fn" font-lock-keyword-face
|
||
"foo" font-lock-function-name-face
|
||
"a" font-lock-variable-name-face
|
||
"u32" font-lock-type-face
|
||
"b" font-lock-variable-name-face
|
||
"u32" font-lock-type-face)))
|
||
|
||
(ert-deftest variable-in-for-loop ()
|
||
(rust-test-font-lock
|
||
"for var in iter"
|
||
'("for" font-lock-keyword-face
|
||
"var" font-lock-variable-name-face
|
||
"in" font-lock-keyword-face))
|
||
(rust-test-font-lock
|
||
"for Foo{var} in iter"
|
||
'("for" font-lock-keyword-face
|
||
"Foo" font-lock-type-face
|
||
"in" font-lock-keyword-face)))
|
||
|
||
(ert-deftest rust-test-dbg-wrap-sexp ()
|
||
"a valid sexp ahead of current pos"
|
||
(rust-test-manip-code
|
||
"let x = add(first, second);"
|
||
15
|
||
#'rust-dbg-wrap-or-unwrap
|
||
"let x = add(dbg!(first), second);"
|
||
24))
|
||
|
||
(ert-deftest rust-test-dbg-wrap-sexp-fallback ()
|
||
"a invalid sexp ahead of current pos"
|
||
;; inside
|
||
(rust-test-manip-code
|
||
"if let Ok(val) = may_val {}"
|
||
27
|
||
#'rust-dbg-wrap-or-unwrap
|
||
"if let Ok(val) = may_val {dbg!()}"
|
||
32)
|
||
;; before
|
||
(rust-test-manip-code
|
||
"let a = {}"
|
||
9
|
||
#'rust-dbg-wrap-or-unwrap
|
||
"let a = dbg!({})"
|
||
17))
|
||
|
||
(ert-deftest rust-test-dbg-wrap-empty-line ()
|
||
(rust-test-manip-code
|
||
"let a = 1;
|
||
|
||
let b = 1;"
|
||
12
|
||
#'rust-dbg-wrap-or-unwrap
|
||
"let a = 1;
|
||
dbg!()
|
||
let b = 1;"
|
||
17))
|
||
|
||
(ert-deftest rust-test-dbg-wrap-empty-before-comment ()
|
||
(rust-test-manip-code
|
||
"let a = 1;
|
||
// comment
|
||
let b = 1;"
|
||
12
|
||
#'rust-dbg-wrap-or-unwrap
|
||
"let a = 1;
|
||
dbg!()// comment
|
||
let b = 1;"
|
||
17)
|
||
;; between statements and comments
|
||
(rust-test-manip-code
|
||
"let a = 1;// comment
|
||
let b = 1;"
|
||
11
|
||
#'rust-dbg-wrap-or-unwrap
|
||
"let a = 1;dbg!()// comment
|
||
let b = 1;"
|
||
16))
|
||
|
||
(ert-deftest rust-test-dbg-wrap-symbol-unbalanced ()
|
||
(rust-test-manip-code
|
||
"let x = add((first, second);"
|
||
14
|
||
#'rust-dbg-wrap-or-unwrap
|
||
"let x = add((dbg!(first), second);"
|
||
25))
|
||
|
||
(ert-deftest rust-test-dbg-wrap-region ()
|
||
(rust-test-manip-code
|
||
"let x = add(first, second);"
|
||
9
|
||
(lambda ()
|
||
(transient-mark-mode 1)
|
||
(push-mark nil t t)
|
||
(goto-char 26)
|
||
(rust-dbg-wrap-or-unwrap))
|
||
"let x = dbg!(add(first, second));"
|
||
33))
|
||
|
||
(defun rust-test-dbg-unwrap (position)
|
||
(rust-test-manip-code
|
||
"let x = add(dbg!(first), second);"
|
||
position
|
||
#'rust-dbg-wrap-or-unwrap
|
||
"let x = add(first, second);"))
|
||
|
||
(ert-deftest rust-test-dbg-uwnrap-within ()
|
||
(rust-test-dbg-unwrap 19))
|
||
|
||
(ert-deftest rust-test-dbg-uwnrap-on-paren ()
|
||
(rust-test-dbg-unwrap 17))
|
||
|
||
(ert-deftest rust-test-dbg-uwnrap-on-dbg-middle ()
|
||
(rust-test-dbg-unwrap 15))
|
||
|
||
(ert-deftest rust-test-dbg-uwnrap-on-dbg-start ()
|
||
(rust-test-dbg-unwrap 13))
|
||
|
||
(ert-deftest rust-test-dbg-unwrap-inside-string-literal ()
|
||
(rust-test-manip-code
|
||
"let x = \"foo, bar\"";"
|
||
15
|
||
#'rust-dbg-wrap-or-unwrap
|
||
"let x = dbg!(\"foo, bar\")"))
|
||
|
||
(ert-deftest rust-test-project-located ()
|
||
(skip-unless (executable-find rust-cargo-bin))
|
||
(let* ((test-dir (expand-file-name "test-project/" default-directory))
|
||
(manifest-file (expand-file-name "Cargo.toml" test-dir)))
|
||
(let ((default-directory test-dir))
|
||
(should (equal (expand-file-name (rust-buffer-project)) manifest-file)))))
|
||
|
||
(defun rust-collect-matches (spec)
|
||
(let ((matches nil))
|
||
(goto-char (point-min))
|
||
(while (re-search-forward (car spec) nil t)
|
||
(push
|
||
(mapcar (lambda (r)
|
||
(let ((match-pos
|
||
(nth (cdr r) spec)))
|
||
(cond ((and (eq :type (car r)) (consp match-pos))
|
||
(compilation-face match-pos))
|
||
((eq :type (car r))
|
||
(cdr (assoc match-pos '((1 . compilation-warning)
|
||
(0 . compilation-info)
|
||
(2 . compilation-error)))))
|
||
((and (null match-pos) (eq :column (car r)))
|
||
'back-to-indentation)
|
||
((and (null match-pos) (eq :file (car r)))
|
||
'like-previous-one)
|
||
((null match-pos)
|
||
(error (format "%S" (car r))))
|
||
(t
|
||
(match-string match-pos)))))
|
||
;; see compilation-error-regexp-alist
|
||
'((:file . 1)
|
||
(:line . 2)
|
||
(:column . 3)
|
||
(:type . 4)
|
||
(:mouse-highlight . 5)))
|
||
matches))
|
||
(nreverse matches)))
|
||
|
||
(ert-deftest compilation-regexp-dashes ()
|
||
(with-temp-buffer
|
||
;; should match
|
||
(insert "error found a -> b\n --> file1.rs:12:34\n\n")
|
||
(insert "error[E1234]: found a -> b\n --> file2.rs:12:34\n\n")
|
||
(insert "warning found a -> b\n --> file3.rs:12:34\n\n")
|
||
(insert "note: `ZZZ` could also refer to the constant imported here -> b\n --> file4.rs:12:34\n\n")
|
||
(insert " ::: file5.rs:12:34\n\n")
|
||
(insert "thread 'main' panicked at src/file7.rs:12:34:\n\n")
|
||
;; should not match
|
||
(insert "werror found a -> b\n --> no_match.rs:12:34\n\n")
|
||
(insert "error[E0061]: this function takes 1 parameter but 2 parameters were supplied\n --> file6.rs:132:34
|
||
|
|
||
82 | fn duration_ms_since(time: &Option<SystemTime>) -> u128 {
|
||
| ------------------------------------------------------- defined here
|
||
...
|
||
132 | self.total_time_ms = duration_ms_since(&self.program_start, 2);
|
||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
")
|
||
(should (equal
|
||
'((("file1.rs" "12" "34" compilation-error "file1.rs:12:34")
|
||
("file2.rs" "12" "34" compilation-error "file2.rs:12:34")
|
||
("file3.rs" "12" "34" compilation-warning "file3.rs:12:34")
|
||
("file4.rs" "12" "34" compilation-info "file4.rs:12:34")
|
||
("file6.rs" "132" "34" compilation-error "file6.rs:132:34"))
|
||
(("file5.rs" "12" "34" compilation-info "file5.rs:12:34"))
|
||
((like-previous-one "82" back-to-indentation compilation-info "82")
|
||
(like-previous-one "132" back-to-indentation compilation-info "132"))
|
||
(("src/file7.rs" "12" "34" nil "src/file7.rs:12:34")))
|
||
(mapcar #'rust-collect-matches
|
||
(list rustc-compilation-regexps
|
||
rustc-colon-compilation-regexps
|
||
rustc-refs-compilation-regexps
|
||
rustc-panics-compilation-regexps))))))
|
||
|
||
;; If electric-pair-mode is available, load it and run the tests that use it. If not,
|
||
;; no error--the tests will be skipped.
|
||
(require 'elec-pair nil t)
|
||
|
||
;; The emacs 23 and 24 versions of ERT do not have test skipping
|
||
;; functionality. So don't even define these tests if elec-pair is
|
||
;; not available.
|
||
(when (featurep 'elec-pair)
|
||
(defun test-electric-pair-insert (original point-pos char closer)
|
||
(let ((old-electric-pair-mode electric-pair-mode))
|
||
(electric-pair-mode 1)
|
||
(unwind-protect
|
||
(with-temp-buffer
|
||
(rust-mode)
|
||
(insert original)
|
||
(font-lock-flush)
|
||
(font-lock-ensure)
|
||
|
||
(goto-char point-pos)
|
||
(deactivate-mark)
|
||
(let ((last-command-event char)) (self-insert-command 1))
|
||
(should (equal (char-after)
|
||
(or closer (aref original point-pos)))))
|
||
(electric-pair-mode (or old-electric-pair-mode 1)))))
|
||
|
||
(ert-deftest rust-test-electric-pair-generic-fn ()
|
||
(test-electric-pair-insert "fn foo() { }" 7 ?< ?>))
|
||
|
||
(ert-deftest rust-test-electric-pair-impl-param ()
|
||
(test-electric-pair-insert "impl Foo<T> for Bar<T>" 5 ?< ?>))
|
||
|
||
(ert-deftest rust-test-electric-pair-impl-for-type-param ()
|
||
(test-electric-pair-insert "impl<T> Foo<T> for Bar" 22 ?< ?>))
|
||
|
||
(ert-deftest rust-test-electric-pair-lt-expression ()
|
||
(test-electric-pair-insert "fn foo(bar:i32) -> bool { bar }" 30 ?< nil))
|
||
|
||
(ert-deftest rust-test-electric-pair-lt-expression-in-struct-literal ()
|
||
(test-electric-pair-insert "fn foo(x:i32) -> Bar { Bar { a:(bleh() + whatever::<X>()), b:x } }" 63 ?< nil))
|
||
|
||
(ert-deftest rust-test-electric-pair-lt-expression-capitalized-keyword ()
|
||
(test-electric-pair-insert "fn foo() -> Box" 16 ?< ?>))
|
||
|
||
(ert-deftest rust-test-electric-pair-<-> ()
|
||
(let ((old-electric-pair-mode electric-pair-mode))
|
||
(electric-pair-mode 1)
|
||
(unwind-protect
|
||
(with-temp-buffer
|
||
(electric-pair-mode 1)
|
||
(rust-mode)
|
||
(mapc (lambda (c)
|
||
(let ((last-command-event c)) (self-insert-command 1)))
|
||
"tmp<T>")
|
||
(should
|
||
(equal "tmp<T>" (buffer-substring-no-properties (point-min)
|
||
(point-max)))))
|
||
(electric-pair-mode (or old-electric-pair-mode 1))))))
|
||
|
||
;; Ensure the byte compiler knows about this function, even though it’s
|
||
;; conditionally-defined.
|
||
(declare-function test-electric-pair-insert "rust-mode-tests"
|
||
(original point-pos char closer))
|
||
|
||
(ert-deftest rust-mode-map ()
|
||
(with-temp-buffer
|
||
(let (from to match (match-count 0))
|
||
(rust-mode)
|
||
(describe-buffer-bindings (current-buffer))
|
||
(goto-char (point-min))
|
||
(re-search-forward "Major Mode Bindings")
|
||
(setq from (point))
|
||
(re-search-forward "")
|
||
(setq to (point))
|
||
(goto-char from)
|
||
(while (re-search-forward "^C-c.*$" to t)
|
||
(setq match-count (1+ match-count))
|
||
(setq match (match-string 0))
|
||
(eval
|
||
`(should
|
||
(or
|
||
(string-match "Prefix Command" ,match)
|
||
(string-match "^C-c C" ,match)))
|
||
t))
|
||
(should (< 0 match-count)))))
|