Files
carbon-lang/toolchain/parse/testdata/packages/import/cpp_inline.carbon
T
Chandler Carruth f0848b1f5e Trailing comments (#7441)
Carbon currently requires a comment to be the only non-whitespace on its
line. A `//` comment that follows other content on a line, called a
_trailing comment_, is a lexer error. This proposal removes that
restriction, allowing a comment to follow other content on a line.
Everything else about comments is unchanged: a comment still begins with
`//`, still requires whitespace after the `//`, and still runs to the
end of the line. Carbon continues to provide only line comments; no
block or intra-line comments are added.

Three observations motivate the change. First, trailing comments are
well suited to short _annotations_ attached to a specific entity or
value on a line. Second, the lexer design now makes it trivial to lex
trailing comments, and in fact requires extra logic and potentially cost
to reject them. Third, C++ code routinely uses trailing comments, so
allowing them lets Carbon carry the layout of migrated code over
directly, rather than reworking each comment to read well in a different
structure.

Implementation notes (beyond the proposal's design):

Keeping trailing comments cheap to lex required a few supporting
changes, all of which keep the cost off the lexer's hot path:

- The lexer already dispatches `//` to comment lexing wherever it
appears, so classifying a comment as trailing is a single O(1) check of
whether the `//` is the line's first non-whitespace (`start + indent`).
The hot comment path is otherwise unchanged.

- That check relies on each line's recorded indentation being its real
leading whitespace. Multi-line string literals previously recorded the
column where the literal opened for the lines they span; they now record
the true (closing-delimiter) indentation instead.

- Parser error recovery (`SkipPastLikelyEnd`) had relied on that
opening-column indentation to keep tokens following a multi-line string
literal attached to the same construct. It now reconstructs that
relationship directly by consulting the line on which the literal
opened, including when other tokens follow the closing delimiter (such
as `''' + "more"`). This is on the cold recovery path.

- `CommentData` records the trailing bit in the high bit of its length
field, keeping it at 8 bytes.

Assisted-by: Claude Code
2026-07-04 06:42:02 +00:00

250 lines
11 KiB
Plaintext

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/packages/import/cpp_inline.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/packages/import/cpp_inline.carbon
// --- fail_bad_syntax.carbon
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:18: error: expected string literal after `inline` [ExpectedStringAfterInline]
// CHECK:STDERR: import Cpp inline;
// CHECK:STDERR: ^
// CHECK:STDERR:
import Cpp inline;
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:19: error: expected string literal after `inline` [ExpectedStringAfterInline]
// CHECK:STDERR: import Cpp inline library "foo.h";
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
import Cpp inline library "foo.h";
// TODO: We might want to allow this as a "preprocessed source" syntax.
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:28: error: `import` declarations must end with a `;` [ExpectedDeclSemi]
// CHECK:STDERR: import Cpp library "foo.h" inline "bar";
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR:
import Cpp library "foo.h" inline "bar";
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:24: error: `import` declarations must end with a `;` [ExpectedDeclSemi]
// CHECK:STDERR: import library "foo.h" inline "bar";
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR:
import library "foo.h" inline "bar";
// --- fail_no_semi_at_eof.carbon
import Cpp inline
// CHECK:STDERR: fail_no_semi_at_eof.carbon:[[@LINE+4]]:1: error: expected string literal after `inline` [ExpectedStringAfterInline]
// CHECK:STDERR:
// CHECK:STDERR: ^
// CHECK:STDERR:
// --- fail_package_inline.carbon
// CHECK:STDERR: fail_package_inline.carbon:[[@LINE+4]]:16: error: `package` declarations must end with a `;` [ExpectedDeclSemi]
// CHECK:STDERR: package NotCpp inline "int n;";
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR:
package NotCpp inline "int n;";
// --- fail_library_inline.carbon
// CHECK:STDERR: fail_library_inline.carbon:[[@LINE+4]]:17: error: `library` declarations must end with a `;` [ExpectedDeclSemi]
// CHECK:STDERR: library "foo.h" inline "int n;";
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR:
library "foo.h" inline "int n;";
// --- wrong_package.carbon
// We reject this in check.
import NotCpp inline "bar";
// --- valid_syntax.carbon
import Cpp inline "int m;";
import Cpp inline '''
// C++ comment.
int n;
''';
// --- inline_cpp.carbon
inline Cpp '''
int n;
''';
// --- fail_inline_cpp_syntax_errors.carbon
// CHECK:STDERR: fail_inline_cpp_syntax_errors.carbon:[[@LINE+4]]:7: error: expected `Cpp` after `inline` [ExpectedCppAfterInline]
// CHECK:STDERR: inline;
// CHECK:STDERR: ^
// CHECK:STDERR:
inline;
// CHECK:STDERR: fail_inline_cpp_syntax_errors.carbon:[[@LINE+4]]:8: error: expected `Cpp` after `inline` [ExpectedCppAfterInline]
// CHECK:STDERR: inline '''
// CHECK:STDERR: ^~~
// CHECK:STDERR:
inline '''
int n;
''';
// Recovery skips to the `;` even when tokens follow the closing `'''` on its
// line, so the `;` ends the declaration rather than starting an empty one.
// CHECK:STDERR: fail_inline_cpp_syntax_errors.carbon:[[@LINE+4]]:8: error: expected `Cpp` after `inline` [ExpectedCppAfterInline]
// CHECK:STDERR: inline '''
// CHECK:STDERR: ^~~
// CHECK:STDERR:
inline '''
int n;
''' + "more";
// CHECK:STDERR: fail_inline_cpp_syntax_errors.carbon:[[@LINE+4]]:11: error: expected string literal after `inline Cpp` [ExpectedStringAfterInlineCpp]
// CHECK:STDERR: inline Cpp;
// CHECK:STDERR: ^
// CHECK:STDERR:
inline Cpp;
// CHECK:STDERR: fail_inline_cpp_syntax_errors.carbon:[[@LINE+4]]:12: error: expected string literal after `inline Cpp` [ExpectedStringAfterInlineCpp]
// CHECK:STDERR: inline Cpp x;
// CHECK:STDERR: ^
// CHECK:STDERR:
inline Cpp x;
inline Cpp '''
int n;
'''
// CHECK:STDERR: fail_inline_cpp_syntax_errors.carbon:[[@LINE+4]]:1: error: `inline` declarations must end with a `;` [ExpectedDeclSemi]
// CHECK:STDERR:
// CHECK:STDERR: ^
// CHECK:STDERR:
// --- eof.carbon
// This split exists to avoid the "no `;` at EOF" diagnostic above pointing to
// the last CHECK:STDOUT line below.
// CHECK:STDOUT: - filename: fail_bad_syntax.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'},
// CHECK:STDOUT: {kind: 'CppPackageName', text: 'Cpp'},
// CHECK:STDOUT: {kind: 'InlineImportBody', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'InlineImportSpecifier', text: 'inline', subtree_size: 2},
// CHECK:STDOUT: {kind: 'ImportDecl', text: ';', has_error: yes, subtree_size: 5},
// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'},
// CHECK:STDOUT: {kind: 'CppPackageName', text: 'Cpp'},
// CHECK:STDOUT: {kind: 'InlineImportBody', text: 'library', has_error: yes},
// CHECK:STDOUT: {kind: 'InlineImportSpecifier', text: 'inline', subtree_size: 2},
// CHECK:STDOUT: {kind: 'ImportDecl', text: ';', has_error: yes, subtree_size: 5},
// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'},
// CHECK:STDOUT: {kind: 'CppPackageName', text: 'Cpp'},
// CHECK:STDOUT: {kind: 'LibraryName', text: '"foo.h"'},
// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2},
// CHECK:STDOUT: {kind: 'ImportDecl', text: ';', has_error: yes, subtree_size: 5},
// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'},
// CHECK:STDOUT: {kind: 'LibraryName', text: '"foo.h"'},
// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2},
// CHECK:STDOUT: {kind: 'ImportDecl', text: ';', has_error: yes, subtree_size: 4},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: fail_no_semi_at_eof.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'},
// CHECK:STDOUT: {kind: 'CppPackageName', text: 'Cpp'},
// CHECK:STDOUT: {kind: 'InlineImportBody', text: '', has_error: yes},
// CHECK:STDOUT: {kind: 'InlineImportSpecifier', text: 'inline', subtree_size: 2},
// CHECK:STDOUT: {kind: 'ImportDecl', text: 'inline', has_error: yes, subtree_size: 5},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: fail_package_inline.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'},
// CHECK:STDOUT: {kind: 'IdentifierPackageName', text: 'NotCpp'},
// CHECK:STDOUT: {kind: 'PackageDecl', text: ';', has_error: yes, subtree_size: 3},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: fail_library_inline.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'LibraryIntroducer', text: 'library'},
// CHECK:STDOUT: {kind: 'LibraryName', text: '"foo.h"'},
// CHECK:STDOUT: {kind: 'LibraryDecl', text: ';', has_error: yes, subtree_size: 3},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: wrong_package.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'},
// CHECK:STDOUT: {kind: 'IdentifierPackageName', text: 'NotCpp'},
// CHECK:STDOUT: {kind: 'InlineImportBody', text: '"bar"'},
// CHECK:STDOUT: {kind: 'InlineImportSpecifier', text: 'inline', subtree_size: 2},
// CHECK:STDOUT: {kind: 'ImportDecl', text: ';', subtree_size: 5},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: valid_syntax.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'},
// CHECK:STDOUT: {kind: 'CppPackageName', text: 'Cpp'},
// CHECK:STDOUT: {kind: 'InlineImportBody', text: '"int m;"'},
// CHECK:STDOUT: {kind: 'InlineImportSpecifier', text: 'inline', subtree_size: 2},
// CHECK:STDOUT: {kind: 'ImportDecl', text: ';', subtree_size: 5},
// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'},
// CHECK:STDOUT: {kind: 'CppPackageName', text: 'Cpp'},
// CHECK:STDOUT: {kind: 'InlineImportBody', text: ''''
// CHECK:STDOUT: // C++ comment.
// CHECK:STDOUT: int n;
// CHECK:STDOUT: ''''},
// CHECK:STDOUT: {kind: 'InlineImportSpecifier', text: 'inline', subtree_size: 2},
// CHECK:STDOUT: {kind: 'ImportDecl', text: ';', subtree_size: 5},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: inline_cpp.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'InlineIntroducer', text: 'inline'},
// CHECK:STDOUT: {kind: 'CppNameExpr', text: 'Cpp'},
// CHECK:STDOUT: {kind: 'InlineImportBody', text: ''''
// CHECK:STDOUT: int n;
// CHECK:STDOUT: ''''},
// CHECK:STDOUT: {kind: 'InlineCppDecl', text: ';', subtree_size: 4},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: fail_inline_cpp_syntax_errors.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'InlineIntroducer', text: 'inline'},
// CHECK:STDOUT: {kind: 'InlineCppDecl', text: ';', has_error: yes, subtree_size: 2},
// CHECK:STDOUT: {kind: 'InlineIntroducer', text: 'inline'},
// CHECK:STDOUT: {kind: 'InlineCppDecl', text: ';', has_error: yes, subtree_size: 2},
// CHECK:STDOUT: {kind: 'InlineIntroducer', text: 'inline'},
// CHECK:STDOUT: {kind: 'InlineCppDecl', text: ';', has_error: yes, subtree_size: 2},
// CHECK:STDOUT: {kind: 'InlineIntroducer', text: 'inline'},
// CHECK:STDOUT: {kind: 'CppNameExpr', text: 'Cpp'},
// CHECK:STDOUT: {kind: 'InlineCppDecl', text: ';', has_error: yes, subtree_size: 3},
// CHECK:STDOUT: {kind: 'InlineIntroducer', text: 'inline'},
// CHECK:STDOUT: {kind: 'CppNameExpr', text: 'Cpp'},
// CHECK:STDOUT: {kind: 'InlineCppDecl', text: ';', has_error: yes, subtree_size: 3},
// CHECK:STDOUT: {kind: 'InlineIntroducer', text: 'inline'},
// CHECK:STDOUT: {kind: 'CppNameExpr', text: 'Cpp'},
// CHECK:STDOUT: {kind: 'InlineImportBody', text: ''''
// CHECK:STDOUT: int n;
// CHECK:STDOUT: ''''},
// CHECK:STDOUT: {kind: 'InlineCppDecl', text: ''''
// CHECK:STDOUT: int n;
// CHECK:STDOUT: '''', has_error: yes, subtree_size: 4},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: eof.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]