bug#81308: 32.0.50; The compilation buffer is over-eager about GCC 16 diagnostics

Arsen Arsenović via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <[email protected]>
Newsgroups gmane.emacs.bugs
Message-ID <[email protected]>
Hi Sean,

Sean Whitton <[email protected]> writes:

> Let us know when you think you are done.  Thanks for working on it.

Apologies for the delay.

This one *should* be correct, but it has one issue I've not been able to
fix.


You'll see compilation.txt, it has the following example on L351.  The
"candidate is" line is the relevant one.

test.cc:8:6: error: no declaration matches ‘void foo::test(int, int, const void*, int)’
    8 | void foo::test(int i, int j, const void *ptr, int k)
      |      ^~~
  • there is 1 candidate
    • candidate is: ‘void foo::test(int, int, void*, int)’
      test.cc:4:10:

compilation-mode seems to, sometimes, interpret that latter location as:

  #s(compilation--message
     (10 4
         (("test.cc" nil) nil (8 (42 8 #2 nil nil) (6 8 #2 nil nil))
          (4 (35 4 #2 nil nil) #1) (1 (7 1 #2 nil nil)))
         nil nil)
     2 nil gnu)

... but not if I isolate it into its own file, or if I
revert-buffer-quick, or if I just reopen the file, sometimes!

I am not sure what's going on here.
-- 
Arsen Arsenović
0001-compile.el-handle-GCC-16-nested-notes.patch (text/x-patch, 16.4 KB)
From ddb738ad87deec5a830e35a19f0b142e24783477 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Arsen=20Arsenovi=C4=87?= <[email protected]>
Date: Sat, 27 Jun 2026 13:43:38 +0200
Subject: [PATCH] compile.el: handle GCC 16 nested notes

* lisp/progmodes/compile.el
(compilation-error-regexp-alist-alist): Add gcc-nested-notes.
* etc/compilation.txt (GCC nested notes): New section.  Contains
example of GCCs nested diagnostic notes, matching the
aforementioned gcc-nested-notes.
---
 etc/compilation.txt       | 245 ++++++++++++++++++++++++++++++++++++++
 lisp/progmodes/compile.el |  48 ++++++++
 2 files changed, 293 insertions(+)

diff --git a/etc/compilation.txt b/etc/compilation.txt
index 323ef8cf3c05..bb88003ed67a 100644
--- a/etc/compilation.txt
+++ b/etc/compilation.txt
@@ -322,6 +322,251 @@ In file included from /usr/include/c++/3.3/backward/warn.h:4,
                  from /usr/include/c++/3.3/backward/iostream.h:31:0,
                  from test_clt.cc:1:
 
+* GCC nested notes
+
+symbol: gcc-nested-notes
+
+Example from https://developers.redhat.com/articles/2026/04/28/gcc-16-improved-error-messages-sarif-output#new_c___error_improvements with and without Unicode
+
+test.cc:8:6: error: no declaration matches 'void foo::test(int, int, const void*, int)'
+    8 | void foo::test(int i, int j, const void *ptr, int k)
+      |      ^~~
+  * there is 1 candidate
+    * candidate is: 'void foo::test(int, int, void*, int)'
+      test.cc:4:10:
+          4 |     void test(int i, int j, void *ptr, int k);
+            |          ^~~~
+      * parameter 3 of candidate has type 'void*'...
+        test.cc:4:35:
+            4 |     void test(int i, int j, void *ptr, int k);
+              |                             ~~~~~~^~~
+      * ...which does not match type 'const void*'
+        test.cc:8:42:
+            8 | void foo::test(int i, int j, const void *ptr, int k)
+              |                              ~~~~~~~~~~~~^~~
+test.cc:1:7: note: 'class foo' defined here
+    1 | class foo
+      |       ^~~
+
+test.cc:8:6: error: no declaration matches ‘void foo::test(int, int, const void*, int)’
+    8 | void foo::test(int i, int j, const void *ptr, int k)
+      |      ^~~
+  • there is 1 candidate
+    • candidate is: ‘void foo::test(int, int, void*, int)’
+      test.cc:4:10:
+          4 |     void test(int i, int j, void *ptr, int k);
+            |          ^~~~
+      • parameter 3 of candidate has type ‘void*’...
+        test.cc:4:35:
+            4 |     void test(int i, int j, void *ptr, int k);
+              |                             ~~~~~~^~~
+      • ...which does not match type ‘const void*’
+        test.cc:8:42:
+            8 | void foo::test(int i, int j, const void *ptr, int k)
+              |                              ~~~~~~~~~~~~^~~
+test.cc:1:7: note: ‘class foo’ defined here
+    1 | class foo
+      |       ^~~
+
+../../gcc/gcc/cp/module.cc: In member function ‘void {anonymous}::trees_out::type_node(tree)’:
+../../gcc/gcc/cp/module.cc:9628:40: error: cannot convert ‘qualifier_set’ to ‘int’ in initialization
+ 9628 |           int quals = type_memfn_quals (type);
+      |                       ~~~~~~~~~~~~~~~~~^~~~~~
+      |                                        |
+      |                                        qualifier_set
+../../gcc/gcc/cp/module.cc:9635:24: error: no match for ‘operator!=’ (operand types are ‘int’ and ‘qualifier_set’)
+ 9635 |               || quals != type_memfn_quals (root)
+      |                  ~~~~~ ^~ ~~~~~~~~~~~~~~~~~~~~~~~
+      |                  |                         |
+      |                  int                       qualifier_set
+  • there are 3 candidates
+    ../../gcc/gcc/cp/module.cc:9635:24:
+     9635 |               || quals != type_memfn_quals (root)
+          |                  ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
+In file included from ../../gcc/gcc/cp/module.cc:215:
+    • candidate 1: ‘bool operator==(const cp_expr&, tree)’ (reversed)
+      ../../gcc/gcc/cp/cp-tree.h:126:1:
+        126 | operator == (const cp_expr &lhs, tree rhs)
+            | ^~~~~~~~
+      • no known conversion for argument 1 from ‘qualifier_set’ to ‘const cp_expr&’
+        ../../gcc/gcc/cp/cp-tree.h:126:29:
+          126 | operator == (const cp_expr &lhs, tree rhs)
+              |              ~~~~~~~~~~~~~~~^~~
+In file included from ../../gcc/gcc/coretypes.h:479,
+                 from ../../gcc/gcc/cp/module.cc:214:
+    • candidate 2: ‘template<class T1, class T2> typename wi::binary_traits<T1, T2>::predicate_result operator!=(const T1&, const T2&)’
+      ../../gcc/gcc/wide-int.h:3853:19:
+       3853 | BINARY_PREDICATE (operator !=, ne_p)
+            |                   ^~~~~~~~
+    • in definition of macro ‘BINARY_PREDICATE’
+       3829 |   OP (const T1 &x, const T2 &y) \
+            |   ^~
+      • template argument deduction/substitution failed:
+      • in definition of macro ‘BINARY_PREDICATE’
+         3829 |   OP (const T1 &x, const T2 &y) \
+              |   ^~
+        • ../../gcc/gcc/wide-int.h: In substitution of ‘template<class T1, class T2> typename wi::binary_traits<T1, T2>::predicate_result operator!=(const T1&, const T2&) [with T1 = int; T2 = qualifier_set]’:
+        • required from here
+          ../../gcc/gcc/cp/module.cc:9635:42:   
+           9635 |               || quals != type_memfn_quals (root)
+                |                                                 ^
+        • error: incomplete type ‘wi::int_traits<qualifier_set>’ used in nested name specifier
+          ../../gcc/gcc/wide-int.h:3853:19:
+           3853 | BINARY_PREDICATE (operator !=, ne_p)
+                |                   ^~~~~~~~
+        • in definition of macro ‘BINARY_PREDICATE’
+           3829 |   OP (const T1 &x, const T2 &y) \
+                |   ^~
+In file included from ../../gcc/gcc/input.h:24,
+                 from ../../gcc/gcc/coretypes.h:507:
+    • candidate 3: ‘bool operator!=(const expanded_location&, const expanded_location&)’
+      ../../gcc/gcc/../libcpp/include/line-map.h:1313:1:
+       1313 | operator!= (const expanded_location &a,
+            | ^~~~~~~~
+      • no known conversion for argument 1 from ‘int’ to ‘const expanded_location&’
+        ../../gcc/gcc/../libcpp/include/line-map.h:1313:38:
+         1313 | operator!= (const expanded_location &a,
+              |             ~~~~~~~~~~~~~~~~~~~~~~~~~^
+../../gcc/gcc/c/c-typeck.cc:8719:38: error: no match for ‘operator|’ (operand types are ‘qualifier_set’ and ‘qualifier_set’)
+ 8719 |                           : ((lquals | rquals) == lquals)))
+      |                               ~~~~~~ ^ ~~~~~~
+      |                               |        |
+      |                               |        qualifier_set
+      |                               qualifier_set
+  • there are 6 candidates
+    ../../gcc/gcc/c/c-typeck.cc:8719:38:
+     8719 |                           : ((lquals | rquals) == lquals)))
+          |                               ~~~~~~~^~~~~~~~
+    • candidate 1: ‘template<class T1, class T2> typename wi::binary_traits<T1, T2>::operator_result operator|(const T1&, const T2&)’
+      ../../gcc/gcc/wide-int.h:3855:18:
+       3855 | BINARY_OPERATOR (operator |, bit_or)
+            |                  ^~~~~~~~
+    • in definition of macro ‘BINARY_OPERATOR’
+       3837 |   OP (const T1 &x, const T2 &y) \
+            |   ^~
+      • template argument deduction/substitution failed:
+      • in definition of macro ‘BINARY_OPERATOR’
+         3837 |   OP (const T1 &x, const T2 &y) \
+              |   ^~
+        • ../../gcc/gcc/wide-int.h: In substitution of ‘template<class T1, class T2> typename wi::binary_traits<T1, T2>::operator_result operator|(const T1&, const T2&) [with T1 = qualifier_set; T2 = qualifier_set]’:
+        • required from here
+          ../../gcc/gcc/c/c-typeck.cc:8719:19:   
+           8719 |                           : ((lquals | rquals) == lquals)))
+                |                                        ^~~~~~
+        • error: incomplete type ‘wi::int_traits<qualifier_set>’ used in nested name specifier
+          ../../gcc/gcc/wide-int.h:3855:18:
+           3855 | BINARY_OPERATOR (operator |, bit_or)
+                |                  ^~~~~~~~
+        • in definition of macro ‘BINARY_OPERATOR’
+           3837 |   OP (const T1 &x, const T2 &y) \
+                |   ^~
+    • candidate 2: ‘dump_flags_t operator|(dump_flags_t, dump_flags_t)’
+      ../../gcc/gcc/dumpfile.h:212:1:
+        212 | operator| (dump_flags_t lhs, dump_flags_t rhs)
+            | ^~~~~~~~
+      • no known conversion for argument 1 from ‘qualifier_set’ to ‘dump_flags_t’ {aka ‘dump_flag’}
+        ../../gcc/gcc/dumpfile.h:212:25:
+          212 | operator| (dump_flags_t lhs, dump_flags_t rhs)
+              |            ~~~~~~~~~~~~~^~~
+    • candidate 3: ‘optgroup_flags_t operator|(optgroup_flags_t, optgroup_flags_t)’
+      ../../gcc/gcc/dumpfile.h:280:1:
+        280 | operator| (optgroup_flags_t lhs, optgroup_flags_t rhs)
+            | ^~~~~~~~
+      • no known conversion for argument 1 from ‘qualifier_set’ to ‘optgroup_flags_t’ {aka ‘optgroup_flag’}
+        ../../gcc/gcc/dumpfile.h:280:29:
+          280 | operator| (optgroup_flags_t lhs, optgroup_flags_t rhs)
+              |            ~~~~~~~~~~~~~~~~~^~~
+    • candidate 4: ‘constexpr cv_qualifier operator|(cv_qualifier, cv_qualifier)’
+      ../../gcc/gcc/tree-core.h:716:1:
+        716 | operator| (cv_qualifier l, cv_qualifier r)
+            | ^~~~~~~~
+      • no known conversion for argument 1 from ‘qualifier_set’ to ‘cv_qualifier’
+        ../../gcc/gcc/tree-core.h:716:25:
+          716 | operator| (cv_qualifier l, cv_qualifier r)
+              |            ~~~~~~~~~~~~~^
+    • candidate 5: ‘constexpr qualifier_set operator|(qualifier_set, cv_qualifier)’
+      ../../gcc/gcc/tree.h:2816:1:
+       2816 | operator| (qualifier_set qs, cv_qualifier cvs)
+            | ^~~~~~~~
+      • no known conversion for argument 2 from ‘qualifier_set’ to ‘cv_qualifier’
+        ../../gcc/gcc/tree.h:2816:43:
+         2816 | operator| (qualifier_set qs, cv_qualifier cvs)
+              |                              ~~~~~~~~~~~~~^~~
+    • candidate 6: ‘constexpr qualifier_set operator|(cv_qualifier, qualifier_set)’
+      ../../gcc/gcc/tree.h:2822:1:
+       2822 | operator| (cv_qualifier cvs, qualifier_set qs)
+            | ^~~~~~~~
+      • no known conversion for argument 1 from ‘qualifier_set’ to ‘cv_qualifier’
+        ../../gcc/gcc/tree.h:2822:25:
+         2822 | operator| (cv_qualifier cvs, qualifier_set qs)
+              |            ~~~~~~~~~~~~~^~~
+
+The g++.dg/diagnostic/pr100716.C example has some more complex cases:
+
+pr100716.C: In function ‘int main()’:
+pr100716.C:34:13: error: no matching function for call to ‘A<int>::f(int)’
+   34 |   A<int>().f(0); // { dg-error "no matching function for call to 'A<int>::f\\(int\\)'" }
+      |   ~~~~~~~~~~^~~
+  • there is 1 candidate
+    • candidate 1: ‘template<class U> void A<T>::f() [with T = int]’
+      pr100716.C:7:12:
+          7 |       void f() {} // { dg-line Af }
+            |            ^
+      • candidate expects 0 arguments, 1 provided
+pr100716.C:37:13: error: no matching function for call to ‘A<int>::g()’
+   37 |   A<int>().g(); // { dg-error "no matching function for call to 'A<int>::g\\(\\)'" }
+      |   ~~~~~~~~~~^~
+  • there is 1 candidate
+    • candidate 1: ‘template<class U> void A<T>::g(U) [with T = int]’
+      pr100716.C:10:12:
+         10 |       void g(U) {} // { dg-line Ag }
+            |            ^
+      • candidate expects 1 argument, 0 provided
+pr100716.C:40:13: error: no matching function for call to ‘B<int>::f()’
+   40 |   B<int>().f(); // { dg-error "no matching function for call to 'B<int>::f\\(\\)'" }
+      |   ~~~~~~~~~~^~
+  • there is 1 candidate
+    • candidate 1: ‘template<class U> void B<T>::f(U) [with T = int]’
+      pr100716.C:17:12:
+         17 |       void f(U) {} // { dg-line Bf }
+            |            ^
+      • candidate expects 1 argument, 0 provided
+pr100716.C:43:13: error: no matching function for call to ‘B<int>::g()’
+   43 |   B<int>().g(); // { dg-error "no matching function for call to 'B<int>::g\\(\\)'" }
+      |   ~~~~~~~~~~^~
+  • there is 1 candidate
+    • candidate 1: ‘template<class U> void B<T>::g(U, T) [with T = int]’
+      pr100716.C:20:12:
+         20 |       void g(U, T) {} // { dg-line Bg }
+            |            ^
+      • candidate expects 2 arguments, 0 provided
+pr100716.C:46:15: error: no matching function for call to ‘B<float>::g(int)’
+   46 |   B<float>().g(0); // { dg-error "no matching function for call to 'B<float>::g\\(int\\)'" }
+      |   ~~~~~~~~~~~~^~~
+  • there is 1 candidate
+    • candidate 1: ‘template<class U> void B<T>::g(U, T) [with T = float]’
+      pr100716.C:20:12:
+         20 |       void g(U, T) {} // { dg-line Bg }
+            |            ^
+      • candidate expects 2 arguments, 1 provided
+pr100716.C:49:8: error: no matching function for call to ‘C::f()’
+   49 |   C().f(); // { dg-error "no matching function for call to 'C::f\\(\\)'" }
+      |   ~~~~~^~
+  • there is 1 candidate
+    • candidate 1: ‘template<class U> void C::f(U)’
+      pr100716.C:26:10:
+         26 |     void f(U) {} // { dg-line Cf }
+            |          ^
+      • candidate expects 1 argument, 0 provided
+pr100716.C:52:8: error: no matching function for call to ‘C::g(int)’
+   52 |   C().g(0); // { dg-error "no matching function for call to 'C::g\\(int\\)'" }
+      |   ~~~~~^~~
+  • there is 1 candidate
+    • candidate 1: ‘template<class U> void C::g()’
+      pr100716.C:29:10:
+         29 |     void g() {} // { dg-line Cg }
+            |          ^
+      • candidate expects 0 arguments, 1 provided
 
 * GNU style
 
diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el
index d10a0d9b100d..318f839ad2e7 100644
--- a/lisp/progmodes/compile.el
+++ b/lisp/progmodes/compile.el
@@ -476,6 +476,54 @@ compilation-error-regexp-alist-alist
      ;; so don't try to match it.
      ": \\*\\*\\* \\[\\(\\(.+?\\):\\([0-9]+\\): .+\\)\\]" 2 3 nil 0 1)
 
+    (gcc-nested-notes
+     ;; GCC 16 and onwards can produce messages that have structured and
+     ;; elaborate nested notes.  These messages are, in fact,
+     ;; informative, and not errors unto themselves.
+     ;;
+     ;; The issue is that these messages can include locations (and
+     ;; indeed do include a location by default), which Emacs may
+     ;; misconstrue as a new diagnostic.  So, catch it early, to
+     ;; consider it an informative message.
+     ,(rx
+       bol
+       ;; The structure of these messages is something like:
+       ;;   INDENT BULLET MESSAGE \n
+       ;;          INDENT LOCATION
+       ;; ... where INDENT are spaces, BULLET is either an ASCII star or
+       ;; a Unicode bullet (U+2022), MESSAGE is informative text and
+       ;; LOCATION is the usual FILE:LINE:COL coordinate.
+       ;;
+       ;; Sample:
+       ;;      • candidate is: ‘void foo::test(int, int, void*, int)’
+       ;;        test.cc:4:10:
+       ;;            4 |     void test(int i, int j, void *ptr, int k);
+
+       ;; Match first line.
+       (group-n 9 (+ "  ")) (| ?* ?\N{BULLET}) space (* nonl) (+ (any "\r\n"))
+
+       ;; Match second line.
+       (backref 9) "  "
+       ;; File name group, from `gnu' rule.
+       (group-n 1
+         ;; Avoid matching the file name as a program in the pattern
+         ;; above by disallowing file names entirely composed of digits.
+         ;; Do not allow file names beginning with a space.
+         (| (not (in "0-9" "\n\t "))
+            (: (+ (in "0-9"))
+               (not (in "0-9" "\n"))))
+         ;; A file name can be composed of any non-newline char, but
+         ;; rule out some valid but unlikely cases, such as a trailing
+         ;; space or a space followed by a -, or a colon followed by a
+         ;; space.
+         (*? (| (not (in "\n :"))
+                (: " " (not (in ?- "/\n")))
+                (: ":" (not (in " \n"))))))
+       ?: (group-n 2 (+ (in "0-9")))              ; Line
+       ?: (group-n 3 (+ (in "0-9")))              ; Column
+       ?:)
+     1 2 3 0)
+
     (gnu
      ;; The `gnu' message syntax is
      ;;   [PROGRAM:]FILE:LINE[-ENDLINE]:[COL[-ENDCOL]:] MESSAGE
-- 
2.55.0
signature.asc (application/pgp-signature, 418 B)
-----BEGIN PGP SIGNATURE-----

iQECBAEWCgCqFiEE/uKz0RP8AKMWLWBhUsKUMB6ixJMFAmqANsAbFIAAAAAABAAO
bWFudTIsMi41KzEuMTIsMiwyXxSAAAAAAC4AKGlzc3Vlci1mcHJAbm90YXRpb25z
Lm9wZW5wZ3AuZmlmdGhob3JzZW1hbi5uZXRGRUUyQjNEMTEzRkMwMEEzMTYyRDYw
NjE1MkMyOTQzMDFFQTJDNDkzEBxhcnNlbkBhYXJzZW4ubWUACgkQUsKUMB6ixJMI
5gEAp78Je1WBDZkKnsaKaddD8m1fd7hPsHk4oyZtI5075RsA/08JWos9tx3BjM7C
w+TjOmsRAzOTVRuac7b6SI7TfIsK
=XLWx
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.