mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-23 13:05:06 +00:00
Add formatter support for call and class definition Arguments
(#6274)
## Summary This PR leverages the `Arguments` AST node introduced in #6259 in the formatter, which ensures that we correctly handle trailing comments in calls, like: ```python f( 1, # comment ) pass ``` (Previously, this was treated as a leading comment on `pass`.) This also allows us to unify the argument handling across calls and class definitions. ## Test Plan A bunch of new fixture tests, plus improved Black compatibility.
This commit is contained in:
parent
b095b7204b
commit
4c53bfe896
19 changed files with 640 additions and 252 deletions
|
@ -2627,6 +2627,34 @@ impl AstNode for Comprehension {
|
||||||
AnyNode::from(self)
|
AnyNode::from(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl AstNode for Arguments {
|
||||||
|
fn cast(kind: AnyNode) -> Option<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
if let AnyNode::Arguments(node) = kind {
|
||||||
|
Some(node)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_ref(kind: AnyNodeRef) -> Option<&Self> {
|
||||||
|
if let AnyNodeRef::Arguments(node) = kind {
|
||||||
|
Some(node)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_node_ref(&self) -> AnyNodeRef {
|
||||||
|
AnyNodeRef::from(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_any_node(self) -> AnyNode {
|
||||||
|
AnyNode::from(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
impl AstNode for Parameters {
|
impl AstNode for Parameters {
|
||||||
fn cast(kind: AnyNode) -> Option<Self>
|
fn cast(kind: AnyNode) -> Option<Self>
|
||||||
where
|
where
|
||||||
|
@ -3458,6 +3486,11 @@ impl From<Comprehension> for AnyNode {
|
||||||
AnyNode::Comprehension(node)
|
AnyNode::Comprehension(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl From<Arguments> for AnyNode {
|
||||||
|
fn from(node: Arguments) -> Self {
|
||||||
|
AnyNode::Arguments(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
impl From<Parameters> for AnyNode {
|
impl From<Parameters> for AnyNode {
|
||||||
fn from(node: Parameters) -> Self {
|
fn from(node: Parameters) -> Self {
|
||||||
AnyNode::Parameters(node)
|
AnyNode::Parameters(node)
|
||||||
|
@ -4909,6 +4942,11 @@ impl<'a> From<&'a Comprehension> for AnyNodeRef<'a> {
|
||||||
AnyNodeRef::Comprehension(node)
|
AnyNodeRef::Comprehension(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<'a> From<&'a Arguments> for AnyNodeRef<'a> {
|
||||||
|
fn from(node: &'a Arguments) -> Self {
|
||||||
|
AnyNodeRef::Arguments(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
impl<'a> From<&'a Parameters> for AnyNodeRef<'a> {
|
impl<'a> From<&'a Parameters> for AnyNodeRef<'a> {
|
||||||
fn from(node: &'a Parameters) -> Self {
|
fn from(node: &'a Parameters) -> Self {
|
||||||
AnyNodeRef::Parameters(node)
|
AnyNodeRef::Parameters(node)
|
||||||
|
|
|
@ -86,3 +86,28 @@ f(
|
||||||
f(
|
f(
|
||||||
a.very_long_function_function_that_is_so_long_that_it_expands_the_parent_but_its_only_a_single_argument()
|
a.very_long_function_function_that_is_so_long_that_it_expands_the_parent_but_its_only_a_single_argument()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
f( # abc
|
||||||
|
)
|
||||||
|
|
||||||
|
f( # abc
|
||||||
|
# abc
|
||||||
|
)
|
||||||
|
|
||||||
|
f(
|
||||||
|
# abc
|
||||||
|
)
|
||||||
|
|
||||||
|
f ( # abc
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
f (
|
||||||
|
# abc
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
f (
|
||||||
|
1
|
||||||
|
# abc
|
||||||
|
)
|
||||||
|
|
|
@ -92,3 +92,55 @@ class Test:
|
||||||
"""Docstring"""
|
"""Docstring"""
|
||||||
# comment
|
# comment
|
||||||
x = 1
|
x = 1
|
||||||
|
|
||||||
|
|
||||||
|
class C(): # comment
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C( # comment
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C(
|
||||||
|
# comment
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C(): # comment
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C( # comment
|
||||||
|
# comment
|
||||||
|
1
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C(
|
||||||
|
1
|
||||||
|
# comment
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
# Copied from transformers.models.clip.modeling_clip.CLIPOutput with CLIP->AltCLIP
|
||||||
|
class AltCLIPOutput(ModelOutput):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AltCLIPOutput( # Copied from transformers.models.clip.modeling_clip.CLIPOutput with CLIP->AltCLIP
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AltCLIPOutput(
|
||||||
|
# Copied from transformers.models.clip.modeling_clip.CLIPOutput with CLIP->AltCLIP
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
|
@ -3,7 +3,7 @@ use ruff_python_ast::Ranged;
|
||||||
use ruff_python_trivia::{SimpleToken, SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_trivia::{SimpleToken, SimpleTokenKind, SimpleTokenizer};
|
||||||
use ruff_text_size::{TextRange, TextSize};
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
||||||
use crate::comments::{dangling_comments, SourceComment};
|
use crate::comments::{dangling_comments, trailing_comments, SourceComment};
|
||||||
use crate::context::{NodeLevel, WithNodeLevel};
|
use crate::context::{NodeLevel, WithNodeLevel};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::MagicTrailingComma;
|
use crate::MagicTrailingComma;
|
||||||
|
@ -252,7 +252,7 @@ impl<'ast> Format<PyFormatContext<'ast>> for EmptyWithDanglingComments<'_> {
|
||||||
[group(&format_args![
|
[group(&format_args![
|
||||||
self.opening,
|
self.opening,
|
||||||
// end-of-line comments
|
// end-of-line comments
|
||||||
dangling_comments(&self.comments[..end_of_line_split]),
|
trailing_comments(&self.comments[..end_of_line_split]),
|
||||||
// own line comments, which need to be indented
|
// own line comments, which need to be indented
|
||||||
soft_block_indent(&dangling_comments(&self.comments[end_of_line_split..])),
|
soft_block_indent(&dangling_comments(&self.comments[end_of_line_split..])),
|
||||||
self.closing
|
self.closing
|
||||||
|
|
|
@ -3,8 +3,8 @@ use std::cmp::Ordering;
|
||||||
use ruff_python_ast::node::AnyNodeRef;
|
use ruff_python_ast::node::AnyNodeRef;
|
||||||
use ruff_python_ast::whitespace::indentation;
|
use ruff_python_ast::whitespace::indentation;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, Comprehension, Expr, ExprAttribute, ExprBinOp, ExprIfExp, ExprSlice, ExprStarred,
|
self as ast, Arguments, Comprehension, Expr, ExprAttribute, ExprBinOp, ExprIfExp, ExprSlice,
|
||||||
MatchCase, Parameters, Ranged,
|
ExprStarred, MatchCase, Parameters, Ranged,
|
||||||
};
|
};
|
||||||
use ruff_python_trivia::{
|
use ruff_python_trivia::{
|
||||||
indentation_at_offset, PythonWhitespace, SimpleToken, SimpleTokenKind, SimpleTokenizer,
|
indentation_at_offset, PythonWhitespace, SimpleToken, SimpleTokenKind, SimpleTokenizer,
|
||||||
|
@ -46,6 +46,7 @@ pub(super) fn place_comment<'a>(
|
||||||
AnyNodeRef::Parameters(arguments) => {
|
AnyNodeRef::Parameters(arguments) => {
|
||||||
handle_parameters_separator_comment(comment, arguments, locator)
|
handle_parameters_separator_comment(comment, arguments, locator)
|
||||||
}
|
}
|
||||||
|
AnyNodeRef::Arguments(arguments) => handle_arguments_comment(comment, arguments),
|
||||||
AnyNodeRef::Comprehension(comprehension) => {
|
AnyNodeRef::Comprehension(comprehension) => {
|
||||||
handle_comprehension_comment(comment, comprehension, locator)
|
handle_comprehension_comment(comment, comprehension, locator)
|
||||||
}
|
}
|
||||||
|
@ -80,6 +81,9 @@ pub(super) fn place_comment<'a>(
|
||||||
AnyNodeRef::StmtFunctionDef(_) | AnyNodeRef::StmtAsyncFunctionDef(_) => {
|
AnyNodeRef::StmtFunctionDef(_) | AnyNodeRef::StmtAsyncFunctionDef(_) => {
|
||||||
handle_leading_function_with_decorators_comment(comment)
|
handle_leading_function_with_decorators_comment(comment)
|
||||||
}
|
}
|
||||||
|
AnyNodeRef::StmtClassDef(class_def) => {
|
||||||
|
handle_leading_class_with_decorators_comment(comment, class_def)
|
||||||
|
}
|
||||||
AnyNodeRef::StmtImportFrom(import_from) => handle_import_from_comment(comment, import_from),
|
AnyNodeRef::StmtImportFrom(import_from) => handle_import_from_comment(comment, import_from),
|
||||||
_ => CommentPlacement::Default(comment),
|
_ => CommentPlacement::Default(comment),
|
||||||
}
|
}
|
||||||
|
@ -843,6 +847,32 @@ fn handle_leading_function_with_decorators_comment(comment: DecoratedComment) ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle comments between decorators and the decorated node.
|
||||||
|
///
|
||||||
|
/// For example, given:
|
||||||
|
/// ```python
|
||||||
|
/// @dataclass
|
||||||
|
/// # comment
|
||||||
|
/// class Foo(Bar):
|
||||||
|
/// ...
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The comment should be attached to the enclosing [`ast::StmtClassDef`] as a dangling node,
|
||||||
|
/// as opposed to being treated as a leading comment on `Bar` or similar.
|
||||||
|
fn handle_leading_class_with_decorators_comment<'a>(
|
||||||
|
comment: DecoratedComment<'a>,
|
||||||
|
class_def: &'a ast::StmtClassDef,
|
||||||
|
) -> CommentPlacement<'a> {
|
||||||
|
if comment.start() < class_def.name.start() {
|
||||||
|
if let Some(decorator) = class_def.decorator_list.last() {
|
||||||
|
if decorator.end() < comment.start() {
|
||||||
|
return CommentPlacement::dangling(class_def, comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CommentPlacement::Default(comment)
|
||||||
|
}
|
||||||
|
|
||||||
/// Handles comments between `**` and the variable name in dict unpacking
|
/// Handles comments between `**` and the variable name in dict unpacking
|
||||||
/// It attaches these to the appropriate value node
|
/// It attaches these to the appropriate value node
|
||||||
///
|
///
|
||||||
|
@ -1105,6 +1135,64 @@ fn find_only_token_in_range(
|
||||||
token
|
token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attach an enclosed end-of-line comment to a set of [`Arguments`].
|
||||||
|
///
|
||||||
|
/// For example, given:
|
||||||
|
/// ```python
|
||||||
|
/// foo( # comment
|
||||||
|
/// bar,
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The comment will be attached to the [`Arguments`] node as a dangling comment, to ensure
|
||||||
|
/// that it remains on the same line as open parenthesis.
|
||||||
|
fn handle_arguments_comment<'a>(
|
||||||
|
comment: DecoratedComment<'a>,
|
||||||
|
arguments: &'a Arguments,
|
||||||
|
) -> CommentPlacement<'a> {
|
||||||
|
// The comment needs to be on the same line, but before the first argument. For example, we want
|
||||||
|
// to treat this as a dangling comment:
|
||||||
|
// ```python
|
||||||
|
// foo( # comment
|
||||||
|
// bar,
|
||||||
|
// baz,
|
||||||
|
// qux,
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
// However, this should _not_ be treated as a dangling comment:
|
||||||
|
// ```python
|
||||||
|
// foo(bar, # comment
|
||||||
|
// baz,
|
||||||
|
// qux,
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
// Thus, we check whether the comment is an end-of-line comment _between_ the start of the
|
||||||
|
// statement and the first argument. If so, the only possible position is immediately following
|
||||||
|
// the open parenthesis.
|
||||||
|
if comment.line_position().is_end_of_line() {
|
||||||
|
let first_argument = match (arguments.args.as_slice(), arguments.keywords.as_slice()) {
|
||||||
|
([first_arg, ..], [first_keyword, ..]) => {
|
||||||
|
if first_arg.start() < first_keyword.start() {
|
||||||
|
Some(first_arg.range())
|
||||||
|
} else {
|
||||||
|
Some(first_keyword.range())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
([first_arg, ..], []) => Some(first_arg.range()),
|
||||||
|
([], [first_keyword, ..]) => Some(first_keyword.range()),
|
||||||
|
([], []) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(first_argument) = first_argument {
|
||||||
|
if arguments.start() < comment.start() && comment.end() < first_argument.start() {
|
||||||
|
return CommentPlacement::dangling(comment.enclosing_node(), comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CommentPlacement::Default(comment)
|
||||||
|
}
|
||||||
|
|
||||||
/// Attach an enclosed end-of-line comment to a [`StmtImportFrom`].
|
/// Attach an enclosed end-of-line comment to a [`StmtImportFrom`].
|
||||||
///
|
///
|
||||||
/// For example, given:
|
/// For example, given:
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
Alias, Comprehension, Decorator, ElifElseClause, ExceptHandler, Expr, Keyword, MatchCase, Mod,
|
Alias, Arguments, Comprehension, Decorator, ElifElseClause, ExceptHandler, Expr, Keyword,
|
||||||
Parameter, ParameterWithDefault, Parameters, Pattern, Ranged, Stmt, TypeParam, WithItem,
|
MatchCase, Mod, Parameter, ParameterWithDefault, Parameters, Pattern, Ranged, Stmt, TypeParam,
|
||||||
|
WithItem,
|
||||||
};
|
};
|
||||||
use ruff_text_size::{TextRange, TextSize};
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
||||||
|
@ -229,6 +230,13 @@ impl<'ast> PreorderVisitor<'ast> for CommentsVisitor<'ast> {
|
||||||
self.finish_node(format_spec);
|
self.finish_node(format_spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_arguments(&mut self, arguments: &'ast Arguments) {
|
||||||
|
if self.start_node(arguments).is_traverse() {
|
||||||
|
walk_arguments(self, arguments);
|
||||||
|
}
|
||||||
|
self.finish_node(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_parameters(&mut self, parameters: &'ast Parameters) {
|
fn visit_parameters(&mut self, parameters: &'ast Parameters) {
|
||||||
if self.start_node(parameters).is_traverse() {
|
if self.start_node(parameters).is_traverse() {
|
||||||
walk_parameters(self, parameters);
|
walk_parameters(self, parameters);
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
use ruff_python_ast::{Arguments, Expr, ExprCall, Ranged};
|
|
||||||
use ruff_text_size::{TextRange, TextSize};
|
|
||||||
|
|
||||||
use crate::builders::empty_parenthesized_with_dangling_comments;
|
|
||||||
use ruff_formatter::write;
|
use ruff_formatter::write;
|
||||||
use ruff_python_ast::node::AnyNodeRef;
|
use ruff_python_ast::ExprCall;
|
||||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
|
||||||
|
|
||||||
use crate::expression::expr_generator_exp::GeneratorExpParentheses;
|
|
||||||
use crate::expression::parentheses::{
|
|
||||||
parenthesized, NeedsParentheses, OptionalParentheses, Parentheses,
|
|
||||||
};
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
|
|
||||||
|
@ -21,136 +12,9 @@ impl FormatNodeRule<ExprCall> for FormatExprCall {
|
||||||
let ExprCall {
|
let ExprCall {
|
||||||
range: _,
|
range: _,
|
||||||
func,
|
func,
|
||||||
arguments:
|
arguments,
|
||||||
Arguments {
|
|
||||||
args,
|
|
||||||
keywords,
|
|
||||||
range: _,
|
|
||||||
},
|
|
||||||
} = item;
|
} = item;
|
||||||
|
|
||||||
// We have a case with `f()` without any argument, which is a special case because we can
|
write!(f, [func.format(), arguments.format()])
|
||||||
// have a comment with no node attachment inside:
|
|
||||||
// ```python
|
|
||||||
// f(
|
|
||||||
// # This function has a dangling comment
|
|
||||||
// )
|
|
||||||
// ```
|
|
||||||
if args.is_empty() && keywords.is_empty() {
|
|
||||||
let comments = f.context().comments().clone();
|
|
||||||
return write!(
|
|
||||||
f,
|
|
||||||
[
|
|
||||||
func.format(),
|
|
||||||
empty_parenthesized_with_dangling_comments(
|
|
||||||
text("("),
|
|
||||||
comments.dangling_comments(item),
|
|
||||||
text(")"),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let all_args = format_with(|f: &mut PyFormatter| {
|
|
||||||
let source = f.context().source();
|
|
||||||
let mut joiner = f.join_comma_separated(item.end());
|
|
||||||
match args.as_slice() {
|
|
||||||
[argument] if keywords.is_empty() => {
|
|
||||||
match argument {
|
|
||||||
Expr::GeneratorExp(generator_exp) => joiner.entry(
|
|
||||||
generator_exp,
|
|
||||||
&generator_exp
|
|
||||||
.format()
|
|
||||||
.with_options(GeneratorExpParentheses::StripIfOnlyFunctionArg),
|
|
||||||
),
|
|
||||||
other => {
|
|
||||||
let parentheses =
|
|
||||||
if is_single_argument_parenthesized(argument, item.end(), source) {
|
|
||||||
Parentheses::Always
|
|
||||||
} else {
|
|
||||||
Parentheses::Never
|
|
||||||
};
|
|
||||||
joiner.entry(other, &other.format().with_options(parentheses))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
arguments => {
|
|
||||||
joiner
|
|
||||||
.entries(
|
|
||||||
// We have the parentheses from the call so the arguments never need any
|
|
||||||
arguments
|
|
||||||
.iter()
|
|
||||||
.map(|arg| (arg, arg.format().with_options(Parentheses::Preserve))),
|
|
||||||
)
|
|
||||||
.nodes(keywords.iter());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
joiner.finish()
|
|
||||||
});
|
|
||||||
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
[
|
|
||||||
func.format(),
|
|
||||||
// The outer group is for things like
|
|
||||||
// ```python
|
|
||||||
// get_collection(
|
|
||||||
// hey_this_is_a_very_long_call,
|
|
||||||
// it_has_funny_attributes_asdf_asdf,
|
|
||||||
// too_long_for_the_line,
|
|
||||||
// really=True,
|
|
||||||
// )
|
|
||||||
// ```
|
|
||||||
// The inner group is for things like:
|
|
||||||
// ```python
|
|
||||||
// get_collection(
|
|
||||||
// hey_this_is_a_very_long_call, it_has_funny_attributes_asdf_asdf, really=True
|
|
||||||
// )
|
|
||||||
// ```
|
|
||||||
// TODO(konstin): Doesn't work see wrongly formatted test
|
|
||||||
parenthesized("(", &group(&all_args), ")")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_dangling_comments(&self, _node: &ExprCall, _f: &mut PyFormatter) -> FormatResult<()> {
|
|
||||||
// Handled in `fmt_fields`
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NeedsParentheses for ExprCall {
|
|
||||||
fn needs_parentheses(
|
|
||||||
&self,
|
|
||||||
_parent: AnyNodeRef,
|
|
||||||
context: &PyFormatContext,
|
|
||||||
) -> OptionalParentheses {
|
|
||||||
self.func.needs_parentheses(self.into(), context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_single_argument_parenthesized(argument: &Expr, call_end: TextSize, source: &str) -> bool {
|
|
||||||
let mut has_seen_r_paren = false;
|
|
||||||
|
|
||||||
for token in
|
|
||||||
SimpleTokenizer::new(source, TextRange::new(argument.end(), call_end)).skip_trivia()
|
|
||||||
{
|
|
||||||
match token.kind() {
|
|
||||||
SimpleTokenKind::RParen => {
|
|
||||||
if has_seen_r_paren {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
has_seen_r_paren = true;
|
|
||||||
}
|
|
||||||
// Skip over any trailing comma
|
|
||||||
SimpleTokenKind::Comma => continue,
|
|
||||||
_ => {
|
|
||||||
// Passed the arguments
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
|
@ -2617,6 +2617,38 @@ impl<'ast> IntoFormat<PyFormatContext<'ast>> for ast::Comprehension {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FormatRule<ast::Arguments, PyFormatContext<'_>> for crate::other::arguments::FormatArguments {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(
|
||||||
|
&self,
|
||||||
|
node: &ast::Arguments,
|
||||||
|
f: &mut Formatter<PyFormatContext<'_>>,
|
||||||
|
) -> FormatResult<()> {
|
||||||
|
FormatNodeRule::<ast::Arguments>::fmt(self, node, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'ast> AsFormat<PyFormatContext<'ast>> for ast::Arguments {
|
||||||
|
type Format<'a> = FormatRefWithRule<
|
||||||
|
'a,
|
||||||
|
ast::Arguments,
|
||||||
|
crate::other::arguments::FormatArguments,
|
||||||
|
PyFormatContext<'ast>,
|
||||||
|
>;
|
||||||
|
fn format(&self) -> Self::Format<'_> {
|
||||||
|
FormatRefWithRule::new(self, crate::other::arguments::FormatArguments::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'ast> IntoFormat<PyFormatContext<'ast>> for ast::Arguments {
|
||||||
|
type Format = FormatOwnedWithRule<
|
||||||
|
ast::Arguments,
|
||||||
|
crate::other::arguments::FormatArguments,
|
||||||
|
PyFormatContext<'ast>,
|
||||||
|
>;
|
||||||
|
fn into_format(self) -> Self::Format {
|
||||||
|
FormatOwnedWithRule::new(self, crate::other::arguments::FormatArguments::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FormatRule<ast::Parameters, PyFormatContext<'_>>
|
impl FormatRule<ast::Parameters, PyFormatContext<'_>>
|
||||||
for crate::other::parameters::FormatParameters
|
for crate::other::parameters::FormatParameters
|
||||||
{
|
{
|
||||||
|
|
152
crates/ruff_python_formatter/src/other/arguments.rs
Normal file
152
crates/ruff_python_formatter/src/other/arguments.rs
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
use ruff_formatter::write;
|
||||||
|
use ruff_python_ast::node::{AnyNodeRef, AstNode};
|
||||||
|
use ruff_python_ast::{Arguments, Expr, ExprCall, Ranged};
|
||||||
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
||||||
|
use crate::builders::empty_parenthesized_with_dangling_comments;
|
||||||
|
use crate::comments::trailing_comments;
|
||||||
|
use crate::expression::expr_generator_exp::GeneratorExpParentheses;
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
parenthesized, NeedsParentheses, OptionalParentheses, Parentheses,
|
||||||
|
};
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::FormatNodeRule;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct FormatArguments;
|
||||||
|
|
||||||
|
impl FormatNodeRule<Arguments> for FormatArguments {
|
||||||
|
fn fmt_fields(&self, item: &Arguments, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
|
// We have a case with `f()` without any argument, which is a special case because we can
|
||||||
|
// have a comment with no node attachment inside:
|
||||||
|
// ```python
|
||||||
|
// f(
|
||||||
|
// # This call has a dangling comment.
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
if item.args.is_empty() && item.keywords.is_empty() {
|
||||||
|
let comments = f.context().comments().clone();
|
||||||
|
return write!(
|
||||||
|
f,
|
||||||
|
[empty_parenthesized_with_dangling_comments(
|
||||||
|
text("("),
|
||||||
|
comments.dangling_comments(item),
|
||||||
|
text(")"),
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the arguments are non-empty, then a dangling comment indicates a comment on the
|
||||||
|
// same line as the opening parenthesis, e.g.:
|
||||||
|
// ```python
|
||||||
|
// f( # This call has a dangling comment.
|
||||||
|
// a,
|
||||||
|
// b,
|
||||||
|
// c,
|
||||||
|
// )
|
||||||
|
let comments = f.context().comments().clone();
|
||||||
|
let dangling_comments = comments.dangling_comments(item.as_any_node_ref());
|
||||||
|
write!(f, [trailing_comments(dangling_comments)])?;
|
||||||
|
|
||||||
|
let all_arguments = format_with(|f: &mut PyFormatter| {
|
||||||
|
let source = f.context().source();
|
||||||
|
let mut joiner = f.join_comma_separated(item.end());
|
||||||
|
match item.args.as_slice() {
|
||||||
|
[arg] if item.keywords.is_empty() => {
|
||||||
|
match arg {
|
||||||
|
Expr::GeneratorExp(generator_exp) => joiner.entry(
|
||||||
|
generator_exp,
|
||||||
|
&generator_exp
|
||||||
|
.format()
|
||||||
|
.with_options(GeneratorExpParentheses::StripIfOnlyFunctionArg),
|
||||||
|
),
|
||||||
|
other => {
|
||||||
|
let parentheses =
|
||||||
|
if is_single_argument_parenthesized(arg, item.end(), source) {
|
||||||
|
Parentheses::Always
|
||||||
|
} else {
|
||||||
|
Parentheses::Never
|
||||||
|
};
|
||||||
|
joiner.entry(other, &other.format().with_options(parentheses))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
args => {
|
||||||
|
joiner
|
||||||
|
.entries(
|
||||||
|
// We have the parentheses from the call so the item never need any
|
||||||
|
args.iter()
|
||||||
|
.map(|arg| (arg, arg.format().with_options(Parentheses::Preserve))),
|
||||||
|
)
|
||||||
|
.nodes(item.keywords.iter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
joiner.finish()
|
||||||
|
});
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
// The outer group is for things like
|
||||||
|
// ```python
|
||||||
|
// get_collection(
|
||||||
|
// hey_this_is_a_very_long_call,
|
||||||
|
// it_has_funny_attributes_asdf_asdf,
|
||||||
|
// too_long_for_the_line,
|
||||||
|
// really=True,
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
// The inner group is for things like:
|
||||||
|
// ```python
|
||||||
|
// get_collection(
|
||||||
|
// hey_this_is_a_very_long_call, it_has_funny_attributes_asdf_asdf, really=True
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
// TODO(konstin): Doesn't work see wrongly formatted test
|
||||||
|
parenthesized("(", &group(&all_arguments), ")")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_dangling_comments(&self, _node: &Arguments, _f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
|
// Handled in `fmt_fields`
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprCall {
|
||||||
|
fn needs_parentheses(
|
||||||
|
&self,
|
||||||
|
_parent: AnyNodeRef,
|
||||||
|
context: &PyFormatContext,
|
||||||
|
) -> OptionalParentheses {
|
||||||
|
self.func.needs_parentheses(self.into(), context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_single_argument_parenthesized(argument: &Expr, call_end: TextSize, source: &str) -> bool {
|
||||||
|
let mut has_seen_r_paren = false;
|
||||||
|
|
||||||
|
for token in
|
||||||
|
SimpleTokenizer::new(source, TextRange::new(argument.end(), call_end)).skip_trivia()
|
||||||
|
{
|
||||||
|
match token.kind() {
|
||||||
|
SimpleTokenKind::RParen => {
|
||||||
|
if has_seen_r_paren {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
has_seen_r_paren = true;
|
||||||
|
}
|
||||||
|
// Skip over any trailing comma
|
||||||
|
SimpleTokenKind::Comma => continue,
|
||||||
|
_ => {
|
||||||
|
// Passed the arguments
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
pub(crate) mod alias;
|
pub(crate) mod alias;
|
||||||
|
pub(crate) mod arguments;
|
||||||
pub(crate) mod comprehension;
|
pub(crate) mod comprehension;
|
||||||
pub(crate) mod decorator;
|
pub(crate) mod decorator;
|
||||||
pub(crate) mod elif_else_clause;
|
pub(crate) mod elif_else_clause;
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
use ruff_python_ast::{Arguments, Ranged, StmtClassDef};
|
|
||||||
use ruff_text_size::TextRange;
|
|
||||||
|
|
||||||
use ruff_formatter::write;
|
use ruff_formatter::write;
|
||||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_ast::{Ranged, StmtClassDef};
|
||||||
|
use ruff_python_trivia::{lines_after, skip_trailing_trivia};
|
||||||
|
|
||||||
use crate::comments::trailing_comments;
|
use crate::comments::{leading_comments, trailing_comments};
|
||||||
use crate::expression::parentheses::{parenthesized, Parentheses};
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::suite::SuiteKind;
|
use crate::statement::suite::SuiteKind;
|
||||||
|
|
||||||
|
@ -23,40 +20,84 @@ impl FormatNodeRule<StmtClassDef> for FormatStmtClassDef {
|
||||||
decorator_list,
|
decorator_list,
|
||||||
} = item;
|
} = item;
|
||||||
|
|
||||||
f.join_with(hard_line_break())
|
let comments = f.context().comments().clone();
|
||||||
.entries(decorator_list.iter().formatted())
|
|
||||||
.finish()?;
|
|
||||||
|
|
||||||
if !decorator_list.is_empty() {
|
let dangling_comments = comments.dangling_comments(item);
|
||||||
hard_line_break().fmt(f)?;
|
let trailing_definition_comments_start =
|
||||||
|
dangling_comments.partition_point(|comment| comment.line_position().is_own_line());
|
||||||
|
|
||||||
|
let (leading_definition_comments, trailing_definition_comments) =
|
||||||
|
dangling_comments.split_at(trailing_definition_comments_start);
|
||||||
|
|
||||||
|
if let Some(last_decorator) = decorator_list.last() {
|
||||||
|
f.join_with(hard_line_break())
|
||||||
|
.entries(decorator_list.iter().formatted())
|
||||||
|
.finish()?;
|
||||||
|
|
||||||
|
if leading_definition_comments.is_empty() {
|
||||||
|
write!(f, [hard_line_break()])?;
|
||||||
|
} else {
|
||||||
|
// Write any leading definition comments (between last decorator and the header)
|
||||||
|
// while maintaining the right amount of empty lines between the comment
|
||||||
|
// and the last decorator.
|
||||||
|
let decorator_end =
|
||||||
|
skip_trailing_trivia(last_decorator.end(), f.context().source());
|
||||||
|
|
||||||
|
let leading_line = if lines_after(decorator_end, f.context().source()) <= 1 {
|
||||||
|
hard_line_break()
|
||||||
|
} else {
|
||||||
|
empty_line()
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[leading_line, leading_comments(leading_definition_comments)]
|
||||||
|
)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(f, [text("class"), space(), name.format()])?;
|
write!(f, [text("class"), space(), name.format()])?;
|
||||||
|
|
||||||
if arguments
|
if let Some(arguments) = arguments {
|
||||||
.as_ref()
|
// Drop empty parentheses, e.g., in:
|
||||||
.is_some_and(|Arguments { args, keywords, .. }| {
|
// ```python
|
||||||
!(args.is_empty() && keywords.is_empty())
|
// class A():
|
||||||
})
|
// ...
|
||||||
{
|
// ```
|
||||||
parenthesized(
|
//
|
||||||
"(",
|
// However, preserve any dangling end-of-line comments, e.g., in:
|
||||||
&FormatInheritanceClause {
|
// ```python
|
||||||
class_definition: item,
|
// class A( # comment
|
||||||
},
|
// ):
|
||||||
")",
|
// ...
|
||||||
)
|
//
|
||||||
.fmt(f)?;
|
// If the arguments contain any dangling own-line comments, we retain the parentheses,
|
||||||
|
// e.g., in:
|
||||||
|
// ```python
|
||||||
|
// class A( # comment
|
||||||
|
// # comment
|
||||||
|
// ):
|
||||||
|
// ...
|
||||||
|
// ```
|
||||||
|
if arguments.args.is_empty()
|
||||||
|
&& arguments.keywords.is_empty()
|
||||||
|
&& comments
|
||||||
|
.dangling_comments(arguments)
|
||||||
|
.iter()
|
||||||
|
.all(|comment| comment.line_position().is_end_of_line())
|
||||||
|
{
|
||||||
|
let dangling = comments.dangling_comments(arguments);
|
||||||
|
write!(f, [trailing_comments(dangling)])?;
|
||||||
|
} else {
|
||||||
|
write!(f, [arguments.format()])?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let comments = f.context().comments().clone();
|
|
||||||
let trailing_head_comments = comments.dangling_comments(item);
|
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
text(":"),
|
text(":"),
|
||||||
trailing_comments(trailing_head_comments),
|
trailing_comments(trailing_definition_comments),
|
||||||
block_indent(&body.format().with_options(SuiteKind::Class))
|
block_indent(&body.format().with_options(SuiteKind::Class))
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -71,58 +112,3 @@ impl FormatNodeRule<StmtClassDef> for FormatStmtClassDef {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FormatInheritanceClause<'a> {
|
|
||||||
class_definition: &'a StmtClassDef,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Format<PyFormatContext<'_>> for FormatInheritanceClause<'_> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
|
||||||
let StmtClassDef {
|
|
||||||
arguments:
|
|
||||||
Some(Arguments {
|
|
||||||
args: bases,
|
|
||||||
keywords,
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
name,
|
|
||||||
body,
|
|
||||||
..
|
|
||||||
} = self.class_definition
|
|
||||||
else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
|
|
||||||
let source = f.context().source();
|
|
||||||
|
|
||||||
let mut joiner = f.join_comma_separated(body.first().unwrap().start());
|
|
||||||
|
|
||||||
if let Some((first, rest)) = bases.split_first() {
|
|
||||||
// Manually handle parentheses for the first expression because the logic in `FormatExpr`
|
|
||||||
// doesn't know that it should disregard the parentheses of the inheritance clause.
|
|
||||||
// ```python
|
|
||||||
// class Test(A) # A is not parenthesized, the parentheses belong to the inheritance clause
|
|
||||||
// class Test((A)) # A is parenthesized
|
|
||||||
// ```
|
|
||||||
// parentheses from the inheritance clause belong to the expression.
|
|
||||||
let tokenizer = SimpleTokenizer::new(source, TextRange::new(name.end(), first.start()))
|
|
||||||
.skip_trivia();
|
|
||||||
|
|
||||||
let left_paren_count = tokenizer
|
|
||||||
.take_while(|token| token.kind() == SimpleTokenKind::LParen)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
// Ignore the first parentheses count
|
|
||||||
let parentheses = if left_paren_count > 1 {
|
|
||||||
Parentheses::Always
|
|
||||||
} else {
|
|
||||||
Parentheses::Never
|
|
||||||
};
|
|
||||||
|
|
||||||
joiner.entry(first, &first.format().with_options(parentheses));
|
|
||||||
joiner.nodes(rest.iter());
|
|
||||||
}
|
|
||||||
|
|
||||||
joiner.nodes(keywords.iter()).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ impl FormatRule<AnyFunctionDefinition<'_>, PyFormatContext<'_>> for FormatAnyFun
|
||||||
let trailing_definition_comments_start =
|
let trailing_definition_comments_start =
|
||||||
dangling_comments.partition_point(|comment| comment.line_position().is_own_line());
|
dangling_comments.partition_point(|comment| comment.line_position().is_own_line());
|
||||||
|
|
||||||
let (leading_function_definition_comments, trailing_definition_comments) =
|
let (leading_definition_comments, trailing_definition_comments) =
|
||||||
dangling_comments.split_at(trailing_definition_comments_start);
|
dangling_comments.split_at(trailing_definition_comments_start);
|
||||||
|
|
||||||
if let Some(last_decorator) = item.decorators().last() {
|
if let Some(last_decorator) = item.decorators().last() {
|
||||||
|
@ -52,10 +52,10 @@ impl FormatRule<AnyFunctionDefinition<'_>, PyFormatContext<'_>> for FormatAnyFun
|
||||||
.entries(item.decorators().iter().formatted())
|
.entries(item.decorators().iter().formatted())
|
||||||
.finish()?;
|
.finish()?;
|
||||||
|
|
||||||
if leading_function_definition_comments.is_empty() {
|
if leading_definition_comments.is_empty() {
|
||||||
write!(f, [hard_line_break()])?;
|
write!(f, [hard_line_break()])?;
|
||||||
} else {
|
} else {
|
||||||
// Write any leading function comments (between last decorator and function header)
|
// Write any leading definition comments (between last decorator and the header)
|
||||||
// while maintaining the right amount of empty lines between the comment
|
// while maintaining the right amount of empty lines between the comment
|
||||||
// and the last decorator.
|
// and the last decorator.
|
||||||
let decorator_end =
|
let decorator_end =
|
||||||
|
@ -69,10 +69,7 @@ impl FormatRule<AnyFunctionDefinition<'_>, PyFormatContext<'_>> for FormatAnyFun
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[leading_line, leading_comments(leading_definition_comments)]
|
||||||
leading_line,
|
|
||||||
leading_comments(leading_function_definition_comments)
|
|
||||||
]
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,31 @@ f(
|
||||||
f(
|
f(
|
||||||
a.very_long_function_function_that_is_so_long_that_it_expands_the_parent_but_its_only_a_single_argument()
|
a.very_long_function_function_that_is_so_long_that_it_expands_the_parent_but_its_only_a_single_argument()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
f( # abc
|
||||||
|
)
|
||||||
|
|
||||||
|
f( # abc
|
||||||
|
# abc
|
||||||
|
)
|
||||||
|
|
||||||
|
f(
|
||||||
|
# abc
|
||||||
|
)
|
||||||
|
|
||||||
|
f ( # abc
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
f (
|
||||||
|
# abc
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
f (
|
||||||
|
1
|
||||||
|
# abc
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Output
|
## Output
|
||||||
|
@ -137,8 +162,9 @@ f(
|
||||||
these_arguments_have_values_that_need_to_break_because_they_are_too_long3=session,
|
these_arguments_have_values_that_need_to_break_because_they_are_too_long3=session,
|
||||||
)
|
)
|
||||||
|
|
||||||
f()
|
f(
|
||||||
# dangling comment
|
# dangling comment
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
f(only=1, short=1, arguments=1)
|
f(only=1, short=1, arguments=1)
|
||||||
|
@ -177,6 +203,28 @@ f(
|
||||||
f(
|
f(
|
||||||
a.very_long_function_function_that_is_so_long_that_it_expands_the_parent_but_its_only_a_single_argument()
|
a.very_long_function_function_that_is_so_long_that_it_expands_the_parent_but_its_only_a_single_argument()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
f() # abc
|
||||||
|
|
||||||
|
f( # abc
|
||||||
|
# abc
|
||||||
|
)
|
||||||
|
|
||||||
|
f(
|
||||||
|
# abc
|
||||||
|
)
|
||||||
|
|
||||||
|
f(1) # abc
|
||||||
|
|
||||||
|
f(
|
||||||
|
# abc
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
f(
|
||||||
|
1
|
||||||
|
# abc
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -130,8 +130,7 @@ a = {
|
||||||
3: True,
|
3: True,
|
||||||
}
|
}
|
||||||
|
|
||||||
x = { # dangling end of line comment
|
x = {} # dangling end of line comment
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -149,8 +149,7 @@ a = (
|
||||||
# Regression test: lambda empty arguments ranges were too long, leading to unstable
|
# Regression test: lambda empty arguments ranges were too long, leading to unstable
|
||||||
# formatting
|
# formatting
|
||||||
(
|
(
|
||||||
lambda: ( #
|
lambda: (), #
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,7 @@ b3 = [
|
||||||
```py
|
```py
|
||||||
# Dangling comment placement in empty lists
|
# Dangling comment placement in empty lists
|
||||||
# Regression test for https://github.com/python/cpython/blob/03160630319ca26dcbbad65225da4248e54c45ec/Tools/c-analyzer/c_analyzer/datafiles.py#L14-L16
|
# Regression test for https://github.com/python/cpython/blob/03160630319ca26dcbbad65225da4248e54c45ec/Tools/c-analyzer/c_analyzer/datafiles.py#L14-L16
|
||||||
a1 = [ # a
|
a1 = [] # a
|
||||||
]
|
|
||||||
a2 = [ # a
|
a2 = [ # a
|
||||||
# b
|
# b
|
||||||
]
|
]
|
||||||
|
|
|
@ -98,6 +98,58 @@ class Test:
|
||||||
"""Docstring"""
|
"""Docstring"""
|
||||||
# comment
|
# comment
|
||||||
x = 1
|
x = 1
|
||||||
|
|
||||||
|
|
||||||
|
class C(): # comment
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C( # comment
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C(
|
||||||
|
# comment
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C(): # comment
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C( # comment
|
||||||
|
# comment
|
||||||
|
1
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C(
|
||||||
|
1
|
||||||
|
# comment
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
# Copied from transformers.models.clip.modeling_clip.CLIPOutput with CLIP->AltCLIP
|
||||||
|
class AltCLIPOutput(ModelOutput):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AltCLIPOutput( # Copied from transformers.models.clip.modeling_clip.CLIPOutput with CLIP->AltCLIP
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AltCLIPOutput(
|
||||||
|
# Copied from transformers.models.clip.modeling_clip.CLIPOutput with CLIP->AltCLIP
|
||||||
|
):
|
||||||
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
## Output
|
## Output
|
||||||
|
@ -116,8 +168,7 @@ class Test((Aaaaaaaaaaaaaaaaa), Bbbbbbbbbbbbbbbb, metaclass=meta):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Test(
|
class Test( # trailing class comment
|
||||||
# trailing class comment
|
|
||||||
Aaaaaaaaaaaaaaaaa, # trailing comment
|
Aaaaaaaaaaaaaaaaa, # trailing comment
|
||||||
# in between comment
|
# in between comment
|
||||||
Bbbbbbbbbbbbbbbb,
|
Bbbbbbbbbbbbbbbb,
|
||||||
|
@ -217,6 +268,56 @@ class Test:
|
||||||
|
|
||||||
# comment
|
# comment
|
||||||
x = 1
|
x = 1
|
||||||
|
|
||||||
|
|
||||||
|
class C: # comment
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C: # comment
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C(
|
||||||
|
# comment
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C: # comment
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C( # comment
|
||||||
|
# comment
|
||||||
|
1
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class C(
|
||||||
|
1
|
||||||
|
# comment
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
# Copied from transformers.models.clip.modeling_clip.CLIPOutput with CLIP->AltCLIP
|
||||||
|
class AltCLIPOutput(ModelOutput):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AltCLIPOutput: # Copied from transformers.models.clip.modeling_clip.CLIPOutput with CLIP->AltCLIP
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AltCLIPOutput(
|
||||||
|
# Copied from transformers.models.clip.modeling_clip.CLIPOutput with CLIP->AltCLIP
|
||||||
|
):
|
||||||
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -215,8 +215,7 @@ del (
|
||||||
) # Completed
|
) # Completed
|
||||||
# Done
|
# Done
|
||||||
|
|
||||||
del ( # dangling end of line comment
|
del () # dangling end of line comment
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -192,8 +192,7 @@ raise aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfk < (
|
||||||
) # the other end
|
) # the other end
|
||||||
# sneaky comment
|
# sneaky comment
|
||||||
|
|
||||||
raise ( # another comment
|
raise () # another comment
|
||||||
)
|
|
||||||
|
|
||||||
raise () # what now
|
raise () # what now
|
||||||
|
|
||||||
|
@ -201,8 +200,9 @@ raise ( # sould I stay here
|
||||||
# just a comment here
|
# just a comment here
|
||||||
) # trailing comment
|
) # trailing comment
|
||||||
|
|
||||||
raise hello() # sould I stay here
|
raise hello( # sould I stay here
|
||||||
# just a comment here # trailing comment
|
# just a comment here
|
||||||
|
) # trailing comment
|
||||||
|
|
||||||
raise (
|
raise (
|
||||||
# sould I stay here
|
# sould I stay here
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue