Subject: bug#24094: 25.1.50; revert-buffer error in CC mode

From: Alan Mackenzie <[email-masked]>
Date: Fri, 29 Jul 2016 21:18:24 +0000

Hello, Richard and Óscar.

I've cracked it!

On Fri, Jul 29, 2016 at 07:41:39PM +0100, Richard Copley wrote:

> Here is a recipe.

> Prepare a file "test0.cpp" as follows: (<<END)
> int main () {
>   int a = 0;
>   int b = 1;
>   int c = 2;
>   int d = 3;
> }
> END

> In a shell: cp test0.cpp test.cpp
> In Emacs: visit test.cpp, transpose "line b" and "line c", save the
> buffer, and put point between the transposed lines (i.e., at the
> beginning of "line b").
> In the shell: cp test0.cpp test.cpp
> In Emacs: revisit test.cpp (C-x C-f M-n RET).

> I hope that helps.

Very much indeed.  What is happening in that sequence is that the C-x C-f
calls the hook after-change-functions without having first called
before-change-functions.  This screws up CC Mode.

The function doing this, insert-file-contents, is called as follows:
(insert-file-contents "test.cpp" t nil nil t)
                                 | |   |   |
                                 | |   |   replace
                                 | |   end
                                 | beg
                                 visit

The section of Finsert_file_contents which calls before-change-functions
(through prepare_to_modify_buffer) looks like this:

  if (NILP (visit) && total > 0)
    {
      if (!NILP (BVAR (current_buffer, file_truename))
          /* Make binding buffer-file-name to nil effective.  */
          && !NILP (BVAR (current_buffer, filename))
          && SAVE_MODIFF >= MODIFF)
        we_locked_file = true;
      prepare_to_modify_buffer (PT, PT, NULL);  <======================
    }

The brace block will not be executed since `visit' is t.

The section of code which calls after-change-functions looks like this:

  if (inserted > 0 && total > 0
      && (NILP (visit) || !NILP (replace)))
    {
      signal_after_change (PT, 0, inserted);     <=====================
      update_compositions (PT, PT, CHECK_BORDER);
    }

The brace block here _will_ get called, since `replace' is non-nil.
There are thus two different, conflicting, conditions governing whether
to call the change hooks.  At a guess, the `if' condition around the
after-change-functions call was modified at some stage, without the same
change being made to the condition around the before-change-functions
call.

I'll look into this further over the weekend.

-- 
Alan Mackenzie (Nuremberg, Germany).