Skip to content

Commit

Permalink
Fix source span on CR characters. (#1021)
Browse files Browse the repository at this point in the history
* Fix source span on CR characters.

* TODO notes

* refactor+test
  • Loading branch information
isoos authored Feb 25, 2022
1 parent 8f2ed24 commit 31c6827
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 18 deletions.
65 changes: 47 additions & 18 deletions lib/src/report/static_analysis.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'dart:io';
import 'dart:math' as math;

import 'package:meta/meta.dart';
import 'package:path/path.dart' as p;
import 'package:source_span/source_span.dart';

Expand Down Expand Up @@ -77,24 +78,12 @@ Future<_AnalysisResult> _analyzePackage(PackageContext context) async {
suggestion:
'To reproduce make sure you are using the [lints_core](https://pub.dev/packages/lints) and '
'run `${context.usesFlutter ? 'flutter analyze' : 'dart analyze'} ${codeProblem.file}`',
spanFn: () {
final sourceFile = SourceFile.fromString(
File(p.join(context.packageDir, codeProblem.file))
.readAsStringSync(),
url: p.join(context.packageDir, codeProblem.file));
try {
// SourceSpans are 0-based, so we subtract 1 from line and column.
final startOffset =
sourceFile.getOffset(codeProblem.line - 1, codeProblem.col - 1);
// Limit the maximum length of the source span.
final length = math.min(codeProblem.length, maxSourceSpanLength);
return sourceFile.span(startOffset, startOffset + length);
} on RangeError {
// Note: This happens if the file contains CR as line terminators.
// If the range is invalid, then we just return null.
return null;
}
},
spanFn: () => sourceSpanFromFile(
path: p.join(context.packageDir, codeProblem.file),
line: codeProblem.line,
col: codeProblem.col,
length: codeProblem.length,
),
);
}

Expand Down Expand Up @@ -129,6 +118,46 @@ Future<_AnalysisResult> _analyzePackage(PackageContext context) async {
}
}

@visibleForTesting
FileSpan? sourceSpanFromFile({
required String path,
required int line,
required int col,
required int length,
}) {
final sourceText = File(path).readAsStringSync();
final sourceFile = SourceFile.fromString(sourceText, url: path);
try {
// SourceSpans are 0-based, so we subtract 1 from line and column.
var startOffset = sourceFile.getOffset(line - 1, col - 1);

// making sure that we don't start on CR line terminator
// TODO: Remove this after https://github.com/dart-lang/source_span/issues/79 gets fixed.
while (startOffset < sourceText.length &&
sourceText.codeUnitAt(startOffset) == 13) {
startOffset++;
}
// Limit the maximum length of the source span.
var newLength = math.min(length, maxSourceSpanLength);
newLength = math.min(newLength, sourceText.length - startOffset);
// making sure that we don't end on CR line terminator
// TODO: Remove this after https://github.com/dart-lang/source_span/issues/79 gets fixed.
while (newLength > 0 &&
sourceText.codeUnitAt(startOffset + newLength - 1) == 13) {
newLength--;
}
if (newLength <= 0) {
// Note: this may happen if the span is entirely CR line terminators.
return null;
}
return sourceFile.span(startOffset, startOffset + newLength);
} on RangeError {
// Note: This happens if the file contains CR as line terminators.
// If the range is invalid, then we just return null.
return null;
}
}

class _AnalysisResult {
final List<Issue> errors;
final List<Issue> warnings;
Expand Down
25 changes: 25 additions & 0 deletions test/report/static_analysis_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:io';

import 'package:pana/src/report/static_analysis.dart';
import 'package:pana/src/utils.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';

void main() {
test('bad cr position', () async {
await withTempDir((dir) async {
final file = File(p.join(dir, 'cr.txt'));
await file.writeAsString('abcd efgh\r\n\r\n1234\r\n\r\nxyz');
final s1 =
sourceSpanFromFile(path: file.path, line: 1, col: 6, length: 7);
expect(s1!.text, 'efgh\r\n');
final s2 =
sourceSpanFromFile(path: file.path, line: 1, col: 10, length: 8);
expect(s2!.text, '\n\r\n1234');
});
});
}

0 comments on commit 31c6827

Please sign in to comment.