Skip to content

Commit 9fed267

Browse files
committed
Read a member macro with a body in a class body
A MEMBER WITH NO TYPE AND A BODY, WHOSE NAME IS NOT THE NAME OF ITS CLASS, READ AS A FUNCTION DEFINITION. C++ gives a member with no decl-specifier-seq three readings, a constructor, a destructor and a conversion function ([class.mem], GCC `cp_parser_member_declaration`, Clang `ParseCXXClassMemberDeclaration`), and a constructor takes the name of its class. `TEST_METHOD(TestReflow) { ... }` in `class TextBufferTests` of terminal, `SERIALIZE_METHODS(AddrInfo, obj) { ... }` of bitcoin, `DENC(bufferlist::const_iterator& p) { ... }` of ceph and `BOOST_CONCEPT_USAGE(WritableIterator) { ... }` of boost are macro invocations with a body, and each read as a function definition with a declarator and no type. C++ cannot produce that tree. The member form before a `;` reads such a name as a macro since a62d1cb, and this commit gives the same reading to the form with a body. THE POPULATION. A census of the class bodies of the corpus at 7c0d954 counts 2,417 members with no type, a body and a name that the class does not carry, in 611 files and 556 distinct trees. 1,878 of them stand in a file with no error, and 1,680 have a name with no lowercase letter. THE REPAIR. The branch that reads `NAME(args) { ... }` where a macro call statement can start now reads it where a member starts as well. The two positions share the test of the brace, and the member adds the guard of the name: `member_macro_name` holds where a member starts, the name has the shape of a macro name with two characters or more, and no class head of the file recorded the name. That is the guard of the member form before a `;`. THE NAME IS THE EVIDENCE, BECAUSE THE ARGUMENTS AND THE BRACE PROVE NOTHING HERE. A constructor definition takes the same arguments and the same brace, so only the name of the class tells the two apart. The class record holds that name. The brace test of the call form stays: a brace that holds a list of expressions with a `;` after it, or with a comma in it, is a declaration and not a body, so `COUNTER(int, x){0};` and `LIST(int, y) { 1, 2 };` in a class body keep the reading of the base. WHAT THE SHAPE OF THE NAME LEAVES OUT, MEASURED WITH A DRAFT THAT DROPS IT. A draft that takes every member name that the class record does not hold changes 1,706 sites in 407 files with no error at 7c0d954, 158 more than this commit. At fd2e2db it changes the same 407 files with no error and 73 files with an error, and its NEW ERROR is 0 as well. Every one of the 158 is a constructor that C++ accepts: 148 in a class whose head ends with a macro, `class MatOp_Identity CV_FINAL : public MatOp {` of opencv, which is the population of tree-sitter#280, 8 whose return type a macro call gives on the line before or which stand in a branch for the documentation only, and 2 of `class alignas(void *) Stmt {` in `Stmt.h` of clang, where 97 class heads stand in one file and the record of 32 names gave up `Stmt` before line 1496. The shape of the name leaves each of them with its constructor. THE NUMBERS AT fd2e2db, measured with the command of each gate step. Changed hash with no error in A: 278 corpus files and 0 compiler test files. With an error in A: 35 files, each with one error before and one error after. NEW ERROR 0, FIXED 0, more error bytes 0. The regions read whole: 1,548 sites in the 278 clean files, each a `function_definition` that becomes a `macro_invocation`, and no other root pair. The sites carry 32 names, and each name is a function-like macro. 1,011 sites have the name in the seed of their own project. 534 sites of terminal use `TEST_METHOD`, `TEST_CLASS_SETUP`, `TEST_CLASS_CLEANUP`, `TEST_METHOD_SETUP` and `TEST_METHOD_CLEANUP`, which the TAEF header `WexTestClass.h` of the Windows test framework defines. The corpus holds no copy of that header, so no seed of the corpus can hold those five names, and the name shape is the only evidence there (tree-sitter#372). 3 sites of cgal use `BOOST_CONCEPT_USAGE`, which boost defines and cgal only uses, so the name stands in the seed of a different project of the corpus (tree-sitter#360, item 6). The 35 files with an error hold 94 more sites of the same shape, read with both binaries: `BOOST_CATCH(...)`, `SFL_CATCH (...)`, `JSON_CATCH(...)` and `STDEXEC_CATCH(...)` after a try macro, and the TAEF methods of terminal. Each of them becomes a macro invocation with a body, and the one error of the file stays. THE SEEDED PASS AND THE BASELINES AT fd2e2db. Each side collects its own seeds, 1,239,343 names in 64 projects, and the two collections are byte-identical. The seeded pass changes the same 278 clean files and the same 35 files with an error, with the same 1,548 sites, NEW ERROR 0 and FIXED 0. Ties 3,110 and dedupe 2,818 did not rise and did not fall. The seed check agrees. The co-validity inventory holds 12 rows on each side, added 0 and removed 0. The census of the gate: 0 detectors rose, `inclass_notype` fell from 2,731 to 1,183 in the stable population, which is the 1,548 sites, and `nested_fn` fell by 3, because the body of a macro invocation is no longer the block of a nested function. The differ: added 0, fell 0, agreement 99.110% on each side. Parse operations 0.99999 of the base, parse time 1.002. THE BASE OF THESE NUMBERS. Each number above was measured at fd2e2db, and the same command gave the same number at 7c0d954 before it, with four commits between the two. The commit sits on 3a9ad80, whose own gate measured 0 changed trees in the corpus, 0 in the compiler tests and 0 under the seeds, so its changed-file list is empty and no file of it meets the 313 files of this commit. WHAT STAYS, AND WHY. Of the 1,878 population sites in a file with no error, 330 keep their tree. 291 are constructors in a class whose head ends with a macro, `class MatOp_Identity CV_FINAL : public MatOp {` of opencv. The record takes the last name of the head, so it holds `CV_FINAL` and not the name of the class. That is the population of tree-sitter#280, and the decision on it belongs to the user. 148 of them stand at a member start, and the draft with no shape guard turns each one into a macro. 143 stand behind a specifier, a macro or a template head, where no token of this scan can start. 7 have an uppercase name and a word between the group and the brace: `BOOST_CNV_STRING_ENABLE(...) const { ... }` of boost, `NS_IMETHOD_(int32_t) ID() override { ... }` of firefox, and `timeout ASIO_PREVENT_MACRO_SUBSTITUTION()` of asio. The scan reads the brace after the group only. 32 have a mixed-case name: a return type that a macro call gives on the line before, a constructor of the class that the other branch of a directive names, and other misses of the record. One pin is new, with fields: the TAEF methods of a test class, the constructor of that class, a field, and `DENC(int x) { }` in `class DENC`, where the record holds the name and the constructor keeps its reading. With the macros defined, GCC 16.2 and Clang 22.1 accept the text. Two syntax snippets are new, with the terminal, bitcoin and ceph shapes.
1 parent 65add10 commit 9fed267

3 files changed

Lines changed: 109 additions & 1 deletion

File tree

‎src/scanner.c‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4023,7 +4023,7 @@ static Invocation scan_macro_invocation(Reader *reader, const char *name, size_t
40234023
// A call in an argument gives a body to the macro, and no function definition starts:
40244024
// `MATCHER_P(IsNode, height, absl::StrCat("height ", height)) {`.
40254025
block = lexer->lookahead == '{';
4026-
} else if (call && valid_symbols[MACRO_CALL_START] && lexer->lookahead == '{') {
4026+
} else if (call && (valid_symbols[MACRO_CALL_START] || member_macro_name) && lexer->lookahead == '{') {
40274027
// A NAME WITH A GROUP AND A BODY IS A MACRO WHERE A CALL STATEMENT CAN START. The other
40284028
// reading of that text is a constructor definition, which has no decl-specifier-seq. At
40294029
// namespace scope C++ takes that definition only with a QUALIFIED name, and GCC gives
@@ -4035,6 +4035,18 @@ static Invocation scan_macro_invocation(Reader *reader, const char *name, size_t
40354035
// unqualified constructor IS correct, the token is not valid, and `S(int x) { }` keeps
40364036
// its reading.
40374037
//
4038+
// IN A CLASS BODY THE NAME IS THE EVIDENCE, AS IT IS FOR THE MEMBER BEFORE A `;`. A
4039+
// member with no decl-specifier-seq has three readings, a constructor, a destructor and
4040+
// a conversion function ([class.mem]), and a constructor takes the name of its class. A
4041+
// member with a body whose name is not the name of its class is a macro:
4042+
// `TEST_METHOD(TestReflow) { ... }` of the TAEF header of terminal,
4043+
// `SERIALIZE_METHODS(AddrInfo, obj) { ... }` of bitcoin, and
4044+
// `DENC(bufferlist::const_iterator& p) { ... }` of ceph. `member_macro_name` holds the
4045+
// same guard as the member form before a `;`: a macro-shaped name of two characters or
4046+
// more that no class head of the file recorded. The record gives up the oldest name
4047+
// after MAX_CLASSES names and misses a head that holds an error, and a name with a
4048+
// lowercase letter keeps its constructor until the record is right.
4049+
//
40384050
// THE ARGUMENTS GIVE NO EVIDENCE HERE, SO THE BRACE MUST. An enumerator list and a
40394051
// braced initializer hold a list of expressions: no `;`, no statement keyword, and no
40404052
// two words in sequence at the top level. Such a brace is a declaration when a `;` comes

‎test/corpus/macros.txt‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,87 @@ struct S {
148148
(identifier)
149149
(number_literal))))))
150150

151+
================================================================================
152+
A member macro with a body in a class body
153+
================================================================================
154+
155+
class TextBufferTests
156+
{
157+
TEST_METHOD(TestReflow)
158+
{
159+
VERIFY(1);
160+
}
161+
162+
TEST_CLASS_SETUP(ClassSetup)
163+
{
164+
return true;
165+
}
166+
167+
TextBufferTests(int x) : _y(x)
168+
{
169+
}
170+
171+
int _y;
172+
};
173+
174+
class DENC
175+
{
176+
DENC(int x)
177+
{
178+
}
179+
};
180+
181+
--------------------------------------------------------------------------------
182+
183+
(translation_unit
184+
(class_specifier
185+
name: (type_identifier)
186+
body: (field_declaration_list
187+
(macro_invocation
188+
name: (identifier)
189+
arguments: (token_tree
190+
(identifier))
191+
body: (compound_statement
192+
(expression_statement
193+
(call_expression
194+
function: (identifier)
195+
arguments: (argument_list
196+
(number_literal))))))
197+
(macro_invocation
198+
name: (identifier)
199+
arguments: (token_tree
200+
(identifier))
201+
body: (compound_statement
202+
(return_statement
203+
(true))))
204+
(function_definition
205+
declarator: (function_declarator
206+
declarator: (identifier)
207+
parameters: (parameter_list
208+
(parameter_declaration
209+
type: (primitive_type)
210+
declarator: (identifier))))
211+
(field_initializer_list
212+
(field_initializer
213+
(field_identifier)
214+
(argument_list
215+
(identifier))))
216+
body: (compound_statement))
217+
(field_declaration
218+
type: (primitive_type)
219+
declarator: (field_identifier))))
220+
(class_specifier
221+
name: (type_identifier)
222+
body: (field_declaration_list
223+
(function_definition
224+
declarator: (function_declarator
225+
declarator: (identifier)
226+
parameters: (parameter_list
227+
(parameter_declaration
228+
type: (primitive_type)
229+
declarator: (identifier))))
230+
body: (compound_statement)))))
231+
151232
================================================================================
152233
A constructor and a parenthesized declarator beside a member macro
153234
================================================================================

‎test/syntax/macros.txt‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,21 @@ class TApplication : public TObject {
2626
int x;
2727
ClassDefOverride(TApplication,0) //GUI application singleton
2828
};
29+
### macro_member_with_a_body
30+
class TextBufferTests {
31+
TEST_METHOD(TestReflow)
32+
{
33+
VERIFY(1);
34+
}
35+
TEST_CLASS_SETUP(ClassSetup) { return true; }
36+
TextBufferTests(int x) : _y(x) { }
37+
int _y;
38+
};
39+
### macro_member_with_a_body_and_two_arguments
40+
struct AddrInfo {
41+
SERIALIZE_METHODS(AddrInfo, obj) { READWRITE(obj.source); }
42+
DENC(bufferlist::const_iterator& p) { decode(v, p); }
43+
};
2944
### macro_pointer_lowercase_name
3045
void f(char* absl_nonnull error, std::string* absl_nullable out);
3146
### macro_call_top_level

0 commit comments

Comments
 (0)