;;; 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) { // 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 where K: Eq + Hash + Borrow, 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, } ")) (ert-deftest indent-align-where-in-comment2 () (let ((rust-indent-where-clause t)) (test-indent "fn foo(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: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 */ } " )) ;; When point is inside an example code block, indent-for-tab-command ;; should reindent the example code. (ert-deftest indent-inside-doc-example () (rust-test-manip-code " /// ``` /// if 2 + 2 == 4 { /// success(); /// } /// ``` " 34 #'indent-for-tab-command " /// ``` /// if 2 + 2 == 4 { /// success(); /// } /// ``` " 38)) ;; Inside example code blocks, hidden lines starting with "# " should ;; be indented as if the "# " wasn't there. (ert-deftest indent-inside-doc-example-hidden-code () (rust-test-manip-code " /// ``` /// # if 2 + 2 == 4 { /// # success(); /// # } /// ``` " 36 #'indent-for-tab-command " /// ``` /// # if 2 + 2 == 4 { /// # success(); /// # } /// ``` " 42)) ;; Inside example code blocks, hidden lines starting with "# " ;; shouldn't affect indentation of non-hidden lines. (ert-deftest indent-inside-doc-example-with-hidden-block () (rust-test-manip-code " /// ``` /// # if 2 + 2 == 4 { /// success(); /// # } /// ``` " 40 #'indent-for-tab-command " /// ``` /// # if 2 + 2 == 4 { /// success(); /// # } /// ``` " 36)) ;; When point is outside the comment, indent-for-tab-command should ;; reindent the comment line without affecting its contents. (ert-deftest indent-outside-doc-example () (rust-test-manip-code " impl Foo { /// ``` /// if 2 + 2 == 4 { /// success(); /// } /// ``` } " 49 #'indent-for-tab-command " impl Foo { /// ``` /// if 2 + 2 == 4 { /// success(); /// } /// ``` } " 53)) (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::();" ;; Only the i32 should have been highlighted. '("i32" font-lock-type-face)) (rust-test-font-lock "foo:: " ;; 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() { 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 "") "")) (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() { }" '((8 10) ;; Angle brackets (11 12) ;; Parens (14 16) ;; Curly braces ))) (ert-deftest rust-test-paren-matching-generic-fn-with-return-value () (rust-test-matching-parens " fn foo() -> bool { false }" '((8 10) ;; Angle brackets (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 ::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 (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> = Some(Ok(4 >> 1)); let b = a.map(|x| x.map(|y| y << 3)); let trick_question = z<<::method(); // First two ) '(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 are not angle brackets 141 ;; The << before 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 { a:Option<(i32,Option)>, b:Option, c:bool } fn f(x:i32,y:Option) { let z:Option = None; let b:Bla = Bla{ a:None, b:None, c:x (30 49) ;; Outer angle brackets of a:Option<...> (42 47) ;; Inner angle brackets of Option (64 66) ;; Angle brackets of Option (102 106) ;; Angle brackets of y:Option (127 131) ;; Angle brackets of z:Option (154 157) ;; Angle brackets of b:Bla ) '(209 ;; less than operator in c:x 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 { Foo{ bar:Bar{ a:3, b:x ) '(92 ;; less than operator x X>() -> Z { } " '((8 23) ;; The angle brackets of foo (20 22 ;; The angle brackets of X )) '(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; type Bar = (Foo, Bletch); type ThisThing = HereYouGo B>,E>>;" '((17 21) ;; Angle brackets of Blah (32 34) ;; Angle brackets of Bar (50 52) ;; Angle brackets of Bletch (70 78) ;; Angle brackets of ThisThing (91 118) ;; Angle brackets of HereYouGo<...> (95 117) ;; Angle brackets of Y (106 111) ;; Angle brackets of B> (108 110) ;; Angle brackets of C (114 116) ;; Angle brackets of E ))) (ert-deftest rust-test-paren-matching-tuple-like-struct () (rust-test-matching-parens " struct A(Option); struct C(Result);" '((17 19) ;; The angle brackets (10 20) ;; The parens of A(); (31 33) ;; The angle brackets of C (41 47) ;; The angle brackets of Result ) '())) (ert-deftest rust-test-paren-matching-in-enum () (rust-test-matching-parens " enum Boo { TupleLike(Option), StructLike{foo: Result} }" '((10 12) ;; Angle brackets of Boo (36 38) ;; Angle brackets of Option (68 74) ;; Angle brackets of Result ))) (ert-deftest rust-test-paren-matching-assoc-type-bounds () (rust-test-matching-parens "impl >> Thing {}" '((6 29) ;; Outer angle brackets of impl (10 28) ;; Outer angle brackets of B> (24 26) ;; Inner angle brackets of C (36 38) ;; Angle brackets of Thing ) )) (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,B>(a:A,b:B) -> bool where B:Trait3+Trait4 { 2 + a < 3 && 3 + b > 11 } fn bluh() where A:Fn()+MyTrait, MyTrait::AssocType = Option { } fn fluh() where C:Fn(i32) -> (i32, i32) + SomeTrait, C::AssocType = OtherThing { }" '((7 30) ;; Angle brackets of foo<...> (23 27) ;; Angle brackets of Trait2 (63 67) ;; Angle brackets of Trait3 (75 79) ;; Angle brackets of Trait4 (121 123) ;; Angle brackets of bluh (147 151) ;; Angle brackets of MyTrait (161 163) ;; Angle brackets of MyTrait (184 189) ;; Angle brackets of Option (203 205) ;; Angle brackets of (250 254) ;; Angle brackets of SomeTrait (282 287) ;; Angle brackets of Option ) '(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) {}" '((38 46)) )) (ert-deftest rust-test-paren-matching-references-and-logical-and () (rust-test-matching-parens " fn ampersand_check(a: &Option, b:bool) -> &Option { a.map(|x| { b && x < 32 }) }" '((31 35) ;; Option (56 60) ;; Option ) '(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 { let z:If = If(a < 3); }" '((17 21) ;; Box (37 42) ;; If ) '(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] {}" '((21 25)))) (ert-deftest rust-test-paren-matching-angle-bracket-inner-reference () (rust-test-matching-parens "fn x() -> Option<&Node> {}" '((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<::Item>" '((29 51) ;; Outer Option<> (30 44) ;; Inner ) '(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) { macro_a!( foo:: ); macro_b![ foo as Option ]; } macro_c!{ struct Boo {} }" '((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) { macro_rules! foo ( foo:: ); macro_rules! bar [ foo as Option ]; } macro_c!{ struct Boo {} }" '((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) { macro_c!{ struct Boo {} ") (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) { macro_c!{ struct Boo {} ") (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() { if !(mem::size_of::() > 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 { 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; }" '((31 35) ;; Angle brackets Foo ))) (ert-deftest rust-test-paren-matching-type-ascription () (rust-test-matching-parens " fn rfc803() { let z = a < b:FunnkyThing; let s = Foo { a: b < 3, b: d:CrazyStuff < 3, c: 2 < x:CrazyStuff } }" '((45 49) ;; FunkyThing (111 115) ;; CrazyStuff (149 154) ;; CrazyStuff ) '(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 where T:std::fmt::Debug { Thing(Option) }" '((13 15) ;; MyEnum (59 61) ;; Option ))) (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 where C::X: D, B:FnMut() -> Option+'a { Foo(a < b) } type Foo where T: Copy = Box; " '((10 15) ;; Boo<'a,T> (39 47) ;; Option<&'a str> (72 78) ;; Option<&'a T> (106 110) ;; Result (125 127) ;; D (149 151) ;; Option (184 186) ;; Foo (207 209) ;; Box ) '(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() {}" '((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" . #() { } 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") (insert "[src/file8.rs:159:5] symbol_value(SOME_VAR) = Some(\n\n") (insert " at file9.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) -> 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")) (("src/file8.rs" "159" "5" compilation-info "src/file8.rs:159:5")) (("file9.rs" "12" "34" compilation-info "file9.rs:12:34"))) (mapcar #'rust-collect-matches (list rustc-compilation-regexps rustc-colon-compilation-regexps rustc-refs-compilation-regexps rustc-panics-compilation-regexps rustc-dbg!-compilation-regexps rustc-backtrace-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 for Bar" 5 ?< ?>)) (ert-deftest rust-test-electric-pair-impl-for-type-param () (test-electric-pair-insert "impl Foo 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::()), 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") (should (equal "tmp" (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)))))