Skip to content

Commit

Permalink
fix for slash_for_doc_comments
Browse files Browse the repository at this point in the history
Change-Id: I05ed8f878736f3afeb545940e047ac03dfcbc1f9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/115771
Commit-Queue: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
  • Loading branch information
pq authored and commit-bot@chromium.org committed Sep 6, 2019
1 parent dc204ec commit 4d0fa1e
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ class AssistProcessor extends BaseProcessor {
await _addProposal_assignToLocalVariable();
await _addProposal_convertClassToMixin();
await _addProposal_convertDocumentationIntoBlock();
await _addProposal_convertDocumentationIntoLine();
if (!_containsErrorCode(
{LintNames.slash_for_doc_comments},
)) {
await _addProposal_convertDocumentationIntoLine();
}
await _addProposal_convertIntoFinalField();
await _addProposal_convertIntoGetter();
await _addProposal_convertListConstructorToListLiteral();
Expand Down Expand Up @@ -464,64 +468,7 @@ class AssistProcessor extends BaseProcessor {
}

Future<void> _addProposal_convertDocumentationIntoLine() async {
Comment comment = node.thisOrAncestorOfType<Comment>();
if (comment == null ||
!comment.isDocumentation ||
comment.tokens.length != 1) {
_coverageMarker();
return;
}
Token token = comment.tokens.first;
if (token.type != TokenType.MULTI_LINE_COMMENT) {
_coverageMarker();
return;
}
String text = token.lexeme;
List<String> lines = text.split('\n');
String prefix = utils.getNodePrefix(comment);
List<String> newLines = <String>[];
bool firstLine = true;
String linePrefix = '';
for (String line in lines) {
if (firstLine) {
firstLine = false;
String expectedPrefix = '/**';
if (!line.startsWith(expectedPrefix)) {
_coverageMarker();
return;
}
line = line.substring(expectedPrefix.length).trim();
if (line.isNotEmpty) {
newLines.add('/// $line');
linePrefix = eol + prefix;
}
} else {
if (line.startsWith(prefix + ' */')) {
break;
}
String expectedPrefix = prefix + ' *';
if (!line.startsWith(expectedPrefix)) {
_coverageMarker();
return;
}
line = line.substring(expectedPrefix.length);
if (line.isEmpty) {
newLines.add('$linePrefix///');
} else {
newLines.add('$linePrefix///$line');
}
linePrefix = eol + prefix;
}
}

var changeBuilder = _newDartChangeBuilder();
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
builder.addReplacement(range.node(comment), (DartEditBuilder builder) {
for (String newLine in newLines) {
builder.write(newLine);
}
});
});
final changeBuilder = await createBuilder_convertDocumentationIntoLine();
_addAssistFromBuilder(
changeBuilder, DartAssistKind.CONVERT_DOCUMENTATION_INTO_LINE);
}
Expand Down Expand Up @@ -3715,20 +3662,6 @@ class AssistProcessor extends BaseProcessor {
return true;
}

/**
* Configures [utils] using given [target].
*/
void _configureTargetLocation(Object target) {
utils.targetClassElement = null;
if (target is AstNode) {
ClassDeclaration targetClassDeclaration =
target.thisOrAncestorOfType<ClassDeclaration>();
if (targetClassDeclaration != null) {
utils.targetClassElement = targetClassDeclaration.declaredElement;
}
}
}

bool _containsErrorCode(Set<String> errorCodes) {
final fileOffset = node.offset;
for (var error in context.resolveResult.errors) {
Expand Down
218 changes: 140 additions & 78 deletions pkg/analysis_server/lib/src/services/correction/base_processor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,77 +61,50 @@ abstract class BaseProcessor {
(session.analysisContext.analysisOptions as AnalysisOptionsImpl)
.experimentStatus;

/// This method does nothing, but we invoke it in places where Dart VM
/// coverage agent fails to provide coverage information - such as almost
/// all "return" statements.
///
/// https://code.google.com/p/dart/issues/detail?id=19912
static void _coverageMarker() {}

/// Configures [utils] using given [target].
void configureTargetLocation(Object target) {
utils.targetClassElement = null;
if (target is AstNode) {
ClassDeclaration targetClassDeclaration =
target.thisOrAncestorOfType<ClassDeclaration>();
if (targetClassDeclaration != null) {
utils.targetClassElement = targetClassDeclaration.declaredElement;
}
}
}

Future<ChangeBuilder>
createBuilder_addTypeAnnotation_VariableDeclaration() async {
AstNode node = this.node;
// prepare VariableDeclarationList
VariableDeclarationList declarationList =
node.thisOrAncestorOfType<VariableDeclarationList>();
if (declarationList == null) {
_coverageMarker();
return null;
}
// may be has type annotation already
if (declarationList.type != null) {
_coverageMarker();
return null;
}
// prepare single VariableDeclaration
List<VariableDeclaration> variables = declarationList.variables;
if (variables.length != 1) {
_coverageMarker();
return null;
createBuilder_addTypeAnnotation_DeclaredIdentifier() async {
DeclaredIdentifier declaredIdentifier =
node.thisOrAncestorOfType<DeclaredIdentifier>();
if (declaredIdentifier == null) {
ForStatement forEach = node.thisOrAncestorMatching(
(node) => node is ForStatement && node.forLoopParts is ForEachParts);
ForEachParts forEachParts = forEach?.forLoopParts;
int offset = node.offset;
if (forEach != null &&
forEachParts.iterable != null &&
offset < forEachParts.iterable.offset) {
declaredIdentifier = forEachParts is ForEachPartsWithDeclaration
? forEachParts.loopVariable
: null;
}
}
VariableDeclaration variable = variables[0];
// must be not after the name of the variable
if (selectionOffset > variable.name.end) {
if (declaredIdentifier == null) {
_coverageMarker();
return null;
}
// we need an initializer to get the type from
Expression initializer = variable.initializer;
if (initializer == null) {
// Ensure that there isn't already a type annotation.
if (declaredIdentifier.type != null) {
_coverageMarker();
return null;
}
DartType type = initializer.staticType;
// prepare type source
if ((type is! InterfaceType || type.isDartCoreNull) &&
type is! FunctionType) {
DartType type = declaredIdentifier.identifier.staticType;
if (type is! InterfaceType && type is! FunctionType) {
_coverageMarker();
return null;
}
configureTargetLocation(node);
_configureTargetLocation(node);

var changeBuilder = _newDartChangeBuilder();
bool validChange = true;
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
Token keyword = declarationList.keyword;
if (keyword?.keyword == Keyword.VAR) {
Token keyword = declaredIdentifier.keyword;
if (keyword.keyword == Keyword.VAR) {
builder.addReplacement(range.token(keyword), (DartEditBuilder builder) {
validChange = builder.writeType(type);
});
} else {
builder.addInsertion(variable.offset, (DartEditBuilder builder) {
builder.addInsertion(declaredIdentifier.identifier.offset,
(DartEditBuilder builder) {
validChange = builder.writeType(type);
builder.write(' ');
});
Expand Down Expand Up @@ -166,7 +139,7 @@ abstract class BaseProcessor {
return null;
}
// prepare type source
configureTargetLocation(node);
_configureTargetLocation(node);

var changeBuilder = _newDartChangeBuilder();
bool validChange = true;
Expand All @@ -180,49 +153,57 @@ abstract class BaseProcessor {
}

Future<ChangeBuilder>
createBuilder_addTypeAnnotation_DeclaredIdentifier() async {
DeclaredIdentifier declaredIdentifier =
node.thisOrAncestorOfType<DeclaredIdentifier>();
if (declaredIdentifier == null) {
ForStatement forEach = node.thisOrAncestorMatching(
(node) => node is ForStatement && node.forLoopParts is ForEachParts);
ForEachParts forEachParts = forEach?.forLoopParts;
int offset = node.offset;
if (forEach != null &&
forEachParts.iterable != null &&
offset < forEachParts.iterable.offset) {
declaredIdentifier = forEachParts is ForEachPartsWithDeclaration
? forEachParts.loopVariable
: null;
}
createBuilder_addTypeAnnotation_VariableDeclaration() async {
AstNode node = this.node;
// prepare VariableDeclarationList
VariableDeclarationList declarationList =
node.thisOrAncestorOfType<VariableDeclarationList>();
if (declarationList == null) {
_coverageMarker();
return null;
}
if (declaredIdentifier == null) {
// may be has type annotation already
if (declarationList.type != null) {
_coverageMarker();
return null;
}
// Ensure that there isn't already a type annotation.
if (declaredIdentifier.type != null) {
// prepare single VariableDeclaration
List<VariableDeclaration> variables = declarationList.variables;
if (variables.length != 1) {
_coverageMarker();
return null;
}
DartType type = declaredIdentifier.identifier.staticType;
if (type is! InterfaceType && type is! FunctionType) {
VariableDeclaration variable = variables[0];
// must be not after the name of the variable
if (selectionOffset > variable.name.end) {
_coverageMarker();
return null;
}
configureTargetLocation(node);
// we need an initializer to get the type from
Expression initializer = variable.initializer;
if (initializer == null) {
_coverageMarker();
return null;
}
DartType type = initializer.staticType;
// prepare type source
if ((type is! InterfaceType || type.isDartCoreNull) &&
type is! FunctionType) {
_coverageMarker();
return null;
}
_configureTargetLocation(node);

var changeBuilder = _newDartChangeBuilder();
bool validChange = true;
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
Token keyword = declaredIdentifier.keyword;
if (keyword.keyword == Keyword.VAR) {
Token keyword = declarationList.keyword;
if (keyword?.keyword == Keyword.VAR) {
builder.addReplacement(range.token(keyword), (DartEditBuilder builder) {
validChange = builder.writeType(type);
});
} else {
builder.addInsertion(declaredIdentifier.identifier.offset,
(DartEditBuilder builder) {
builder.addInsertion(variable.offset, (DartEditBuilder builder) {
validChange = builder.writeType(type);
builder.write(' ');
});
Expand All @@ -231,6 +212,68 @@ abstract class BaseProcessor {
return validChange ? changeBuilder : null;
}

Future<ChangeBuilder> createBuilder_convertDocumentationIntoLine() async {
Comment comment = node.thisOrAncestorOfType<Comment>();
if (comment == null ||
!comment.isDocumentation ||
comment.tokens.length != 1) {
_coverageMarker();
return null;
}
Token token = comment.tokens.first;
if (token.type != TokenType.MULTI_LINE_COMMENT) {
_coverageMarker();
return null;
}
String text = token.lexeme;
List<String> lines = text.split('\n');
String prefix = utils.getNodePrefix(comment);
List<String> newLines = <String>[];
bool firstLine = true;
String linePrefix = '';
for (String line in lines) {
if (firstLine) {
firstLine = false;
String expectedPrefix = '/**';
if (!line.startsWith(expectedPrefix)) {
_coverageMarker();
return null;
}
line = line.substring(expectedPrefix.length).trim();
if (line.isNotEmpty) {
newLines.add('/// $line');
linePrefix = eol + prefix;
}
} else {
if (line.startsWith(prefix + ' */')) {
break;
}
String expectedPrefix = prefix + ' *';
if (!line.startsWith(expectedPrefix)) {
_coverageMarker();
return null;
}
line = line.substring(expectedPrefix.length);
if (line.isEmpty) {
newLines.add('$linePrefix///');
} else {
newLines.add('$linePrefix///$line');
}
linePrefix = eol + prefix;
}
}

var changeBuilder = _newDartChangeBuilder();
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
builder.addReplacement(range.node(comment), (DartEditBuilder builder) {
for (String newLine in newLines) {
builder.write(newLine);
}
});
});
return changeBuilder;
}

@protected
Future<ChangeBuilder> createBuilder_useCurlyBraces() async {
Future<ChangeBuilder> doStatement(DoStatement node) async {
Expand Down Expand Up @@ -365,6 +408,25 @@ abstract class BaseProcessor {
return node != null;
}

/// Configures [utils] using given [target].
void _configureTargetLocation(Object target) {
utils.targetClassElement = null;
if (target is AstNode) {
ClassDeclaration targetClassDeclaration =
target.thisOrAncestorOfType<ClassDeclaration>();
if (targetClassDeclaration != null) {
utils.targetClassElement = targetClassDeclaration.declaredElement;
}
}
}

DartChangeBuilder _newDartChangeBuilder() =>
DartChangeBuilderImpl.forWorkspace(workspace);

/// This method does nothing, but we invoke it in places where Dart VM
/// coverage agent fails to provide coverage information - such as almost
/// all "return" statements.
///
/// https://code.google.com/p/dart/issues/detail?id=19912
static void _coverageMarker() {}
}
2 changes: 2 additions & 0 deletions pkg/analysis_server/lib/src/services/correction/fix.dart
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ class DartFixKind {
const FixKind('CONVERT_FLUTTER_CHILD', 50, "Convert to children:");
static const CONVERT_FLUTTER_CHILDREN =
const FixKind('CONVERT_FLUTTER_CHILDREN', 50, "Convert to child:");
static const CONVERT_TO_LINE_COMMENT = const FixKind(
'CONVERT_TO_LINE_COMMENT', 50, "Convert to line documentation comment");
static const CONVERT_TO_NAMED_ARGUMENTS = const FixKind(
'CONVERT_TO_NAMED_ARGUMENTS', 50, "Convert to named arguments");
static const CREATE_CLASS =
Expand Down
Loading

0 comments on commit 4d0fa1e

Please sign in to comment.