Skip to content

refactor: migrate named_parameters_ordering#298

Open
solid-illiaaihistov wants to merge 4 commits into
solid-software:analysis_server_migrationfrom
solid-illiaaihistov:253-migrate-named_parameters_ordering
Open

refactor: migrate named_parameters_ordering#298
solid-illiaaihistov wants to merge 4 commits into
solid-software:analysis_server_migrationfrom
solid-illiaaihistov:253-migrate-named_parameters_ordering

Conversation

@solid-illiaaihistov

Copy link
Copy Markdown
Collaborator

Closes #253

@solid-illiaaihistov solid-illiaaihistov linked an issue Jun 23, 2026 that may be closed by this pull request
@solid-illiaaihistov

Copy link
Copy Markdown
Collaborator Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements the named_parameters_ordering lint rule and its corresponding quick fix to enforce consistent ordering of named parameters. The feedback highlights a critical bug where omitting parameter types in custom configurations leads to sorting errors, and another bug where the quick fix deletes trailing comments. Additionally, the reviewer suggests reusing the configuration parser in the quick fix to prevent duplicate logic and removing an unreachable case in the parameter classification switch statement.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread lib/src/lints/named_parameters_ordering/config_parser.dart
Comment thread lib/src/lints/named_parameters_ordering/fixes/named_parameters_ordering_fix.dart Outdated
Comment thread lib/src/lints/named_parameters_ordering/fixes/named_parameters_ordering_fix.dart Outdated
Comment thread lib/src/lints/named_parameters_ordering/fixes/named_parameters_ordering_fix.dart Outdated
Comment thread lib/src/lints/named_parameters_ordering/models/parameter_type.dart

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new quick fix (NamedParametersOrderingFix) and refactors the rule and visitor (NamedParametersOrderingRule, NamedParametersOrderingVisitor) to enforce and automatically sort named parameters in Dart. It also adds comprehensive unit tests. The review feedback highlights three critical issues: first, unlisted parameter types in the sorting configuration can cause indexOf to return -1, leading to incorrect sorting in the quick fix; second, a similar issue in the visitor's ordering check results in incorrect lint reports; and third, extracting parameter blocks with comments can incorrectly group trailing comments from previous lines, potentially corrupting the code replacement.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread lib/src/lints/named_parameters_ordering/fixes/named_parameters_ordering_fix.dart Outdated
…comments and fix configuration parsing logic
@solid-illiaaihistov

Copy link
Copy Markdown
Collaborator Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the named_parameters_ordering lint rule and its corresponding quick fix (NamedParametersOrderingFix) to enforce named parameter ordering conventions in Dart. The review feedback highlights several edge cases in the quick fix implementation, including potential issues with trailing comma handling, incorrect line prefix calculations for inline parameters, trailing comment attribution, and the potential loss of inline comments in single-line parameter lists. Additionally, it suggests deduplicating the parsed order configuration to prevent unexpected sorting behavior.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread lib/src/lints/named_parameters_ordering/fixes/named_parameters_ordering_fix.dart Outdated
Comment thread lib/src/lints/named_parameters_ordering/config_parser.dart
Comment thread lib/src/lints/named_parameters_ordering/fixes/named_parameters_ordering_fix.dart Outdated
… and ensure unique parameter types in config parser
@solid-illiaaihistov

Copy link
Copy Markdown
Collaborator Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the named_parameters_ordering lint rule and its associated quick fix, NamedParametersOrderingFix, which automatically sorts named parameters according to a specified or default order. Feedback on the implementation highlights two key improvements for the quick fix: implementing a stable sort to preserve the original relative order of parameters with the same type, and using a loop to correctly skip all trailing comments on the same line as a previous parameter.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread lib/src/lints/named_parameters_ordering/fixes/named_parameters_ordering_fix.dart Outdated
Comment on lines +52 to +56
final previousParameterType = parametersInfo.lastOrNull;

final isWrong = _isOrderingWrong(parameterType, previousParameterType);

if (isWrong && previousParameterType != null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
final previousParameterType = parametersInfo.lastOrNull;
final isWrong = _isOrderingWrong(parameterType, previousParameterType);
if (isWrong && previousParameterType != null) {
final previousParameterType = parametersInfo.lastOrNull;
parametersInfo.add(parameterType);
if (previousParameterType == null) continue;
final isWrong = _isOrderingWrong(parameterType, previousParameterType);
if (!isWrong) continue;
... what was nested in the if previously

also instead of parametersInfo, we can add a small extension

extension IterablePairwise<T> on Iterable<T> {
  Iterable<(T, T)> pairwise() sync* {
    for (var i = 0; i + 1 < length; i++) {
      yield (elementAt(i), elementAt(i + 1));
    }
  }
}

that way we can skip null check

final badParameters = namedParametersList
  .pairwise()
  .where((pair) => _isOrderingWrong(pair.$2, pair.$1);

for (final parameter in badParameters) {
  _rule.reportAtNode...
}

Comment on lines +46 to +54

final sortedNamedParams = [...namedParams];
sortedNamedParams.sort((a, b) {
final typeA = ParameterType.fromParameter(a);
final typeB = ParameterType.fromParameter(b);
final indexA = parametersOrder.indexOf(typeA);
final indexB = parametersOrder.indexOf(typeB);
return indexA.compareTo(indexB);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
final sortedNamedParams = [...namedParams];
sortedNamedParams.sort((a, b) {
final typeA = ParameterType.fromParameter(a);
final typeB = ParameterType.fromParameter(b);
final indexA = parametersOrder.indexOf(typeA);
final indexB = parametersOrder.indexOf(typeB);
return indexA.compareTo(indexB);
});
final sortedNamedParams = namedParams.sortedBy(
(e) => parametersOrder.indexOf(ParameterType.fromParameter(e)),
);

Comment on lines +57 to +63
bool isChanged = false;
for (int i = 0; i < namedParams.length; i++) {
if (namedParams[i] != sortedNamedParams[i]) {
isChanged = true;
break;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool isChanged = false;
for (int i = 0; i < namedParams.length; i++) {
if (namedParams[i] != sortedNamedParams[i]) {
isChanged = true;
break;
}
}
final isChanged = namedParams
.zipWith(sortedNamedParams)
.any((pair) => pair.$1 != pair.$2);
extension IterableZip<T> on Iterable<T> {
  Iterable<(T, T)> zipWith(Iterable<T> other) sync* {
    for (var i = 0; i < length && i < other.length; i++) {
      yield (elementAt(i), other.elementAt(i));
    }
  }
}

Comment on lines +74 to +91
if (!isMultiline && !hasComments) {
// Single-line: no leading comments, simple text replacement
final sortedTexts = sortedNamedParams
.map((p) => utils.getRangeText(p.sourceRange))
.toList();

final replacementText = sortedTexts.join(', ');

final targetRange = SourceRange(
namedParams.first.offset,
namedParams.last.end - namedParams.first.offset,
);

await builder.addDartFileEdit(file, (builder) {
builder.addSimpleReplacement(targetRange, replacementText);
});
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sourceStart and sourceEnd are used later in code too

Suggested change
if (!isMultiline && !hasComments) {
// Single-line: no leading comments, simple text replacement
final sortedTexts = sortedNamedParams
.map((p) => utils.getRangeText(p.sourceRange))
.toList();
final replacementText = sortedTexts.join(', ');
final targetRange = SourceRange(
namedParams.first.offset,
namedParams.last.end - namedParams.first.offset,
);
await builder.addDartFileEdit(file, (builder) {
builder.addSimpleReplacement(targetRange, replacementText);
});
return;
}
final sourceStart = namedParams.first.offset;
final sourceEnd = namedParams.last.end;
if (!isMultiline && !hasComments) {
return builder.addDartFileEdit(file, (builder) {
builder.addSimpleReplacement(
SourceRange(sourceStart, sourceEnd - sourceStart),
sortedNamedParams
.map((p) => utils.getRangeText(p.sourceRange))
.toList()
.join(', '),
);
});
}

Comment on lines +109 to +124
final buffer = StringBuffer();
for (int i = 0; i < sortedBlocks.length; i++) {
buffer.write(sortedBlocks[i].text);

final isLast = i == sortedBlocks.length - 1;
if (!isLast || hasTrailingComma) {
buffer.write(',');
}
final trailingComment = sortedBlocks[i].trailingComment;
if (trailingComment != null) {
buffer.write(' $trailingComment');
}
if (!isLast) {
buffer.write('\n');
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
final buffer = StringBuffer();
for (int i = 0; i < sortedBlocks.length; i++) {
buffer.write(sortedBlocks[i].text);
final isLast = i == sortedBlocks.length - 1;
if (!isLast || hasTrailingComma) {
buffer.write(',');
}
final trailingComment = sortedBlocks[i].trailingComment;
if (trailingComment != null) {
buffer.write(' $trailingComment');
}
if (!isLast) {
buffer.write('\n');
}
}
final replacement = sortedBlocks.expandIndexed((i, e) {
final isLast = i == sortedBlocks.length - 1;
final trailingComment = e.trailingComment;
return [
e.text,
if (!isLast || hasTrailingComma) ',',
if (trailingComment != null) ' $trailingComment',
if (!isLast) '\n',
];
}).join();

// comment on the original last parameter's line.
var rangeEnd = namedParams.last.end;
if (hasTrailingComma) {
rangeEnd = namedParams.last.endToken.next!.end;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably extract final tokenAfterEnd = namedParams.last.endToken.next; cuz it's used twice, and make this

    var rangeEnd = hasTrailingComma ? tokenAfterEnd!.end : sourceEnd;

parameterList.rightParenthesis.offset;
if (rangeEnd < upperBound) {
final afterLast = utils.getRangeText(
SourceRange(rangeEnd, upperBound - rangeEnd),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that we are constantly doing SourceRange(start, end - start),

why don't we extract a simple utility SourceRange _createRange(int start, int end) => SourceRange(start, end - start);

Same stuff:
String _getRangeText(int start, int end) => utils.getRangeText(_createRange(start, end));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate named_parameters_ordering

2 participants