Analyzer: add ignore_for_file comment that ignores a type of problem for the whole file; add tests.

BUG=
R=brianwilkerson@google.com

Review-Url: https://codereview.chromium.org/2888953002 .
This commit is contained in:
David Morgan 2017-05-19 10:01:52 +02:00
parent 5182eeab26
commit 32c4094d04
3 changed files with 75 additions and 5 deletions

View file

@ -3005,7 +3005,8 @@ class GenerateLintsTask extends SourceBasedAnalysisTask {
}
/**
* Information about analysis `//ignore:` comments within a source file.
* Information about analysis `//ignore:` and `//ignore_for_file` comments
* within a source file.
*/
class IgnoreInfo {
/**
@ -3024,18 +3025,36 @@ class IgnoreInfo {
static final RegExp _IGNORE_MATCHER =
new RegExp(r'//[ ]*ignore:(.*)$', multiLine: true);
/**
* A regular expression for matching 'ignore_for_file' comments. Produces
* matches containing 2 groups. For example:
*
* * ['//ignore_for_file: error_code', 'error_code']
*
* Resulting codes may be in a list ('error_code_1,error_code2').
*/
static final RegExp _IGNORE_FOR_FILE_MATCHER =
new RegExp(r'//[ ]*ignore_for_file:(.*)$', multiLine: true);
final Map<int, List<String>> _ignoreMap = new HashMap<int, List<String>>();
final Set<String> _ignoreForFileSet = new HashSet<String>();
/**
* Whether this info object defines any ignores.
*/
bool get hasIgnores => ignores.isNotEmpty;
bool get hasIgnores => ignores.isNotEmpty || _ignoreForFileSet.isNotEmpty;
/**
* Map of line numbers to associated ignored error codes.
*/
Map<int, Iterable<String>> get ignores => _ignoreMap;
/**
* Iterable of error codes ignored for the whole file.
*/
Iterable<String> get ignoreForFiles => _ignoreForFileSet;
/**
* Ignore this [errorCode] at [line].
*/
@ -3050,10 +3069,18 @@ class IgnoreInfo {
_ignoreMap.putIfAbsent(line, () => new List<String>()).addAll(errorCodes);
}
/**
* Ignore these [errorCodes] in the whole file.
*/
void addAllForFile(Iterable<String> errorCodes) {
_ignoreForFileSet.addAll(errorCodes);
}
/**
* Test whether this [errorCode] is ignored at the given [line].
*/
bool ignoredAt(String errorCode, int line) =>
_ignoreForFileSet.contains(errorCode) ||
_ignoreMap[line]?.contains(errorCode) == true;
/**
@ -3061,7 +3088,8 @@ class IgnoreInfo {
*/
static IgnoreInfo calculateIgnores(String content, LineInfo info) {
Iterable<Match> matches = _IGNORE_MATCHER.allMatches(content);
if (matches.isEmpty) {
Iterable<Match> fileMatches = _IGNORE_FOR_FILE_MATCHER.allMatches(content);
if (matches.isEmpty && fileMatches.isEmpty) {
return _EMPTY_INFO;
}
@ -3074,6 +3102,13 @@ class IgnoreInfo {
.map((String code) => code.trim().toLowerCase());
ignoreInfo.addAll(info.getLocation(match.start).lineNumber, codes);
}
for (Match match in fileMatches) {
Iterable<String> codes = match
.group(1)
.split(',')
.map((String code) => code.trim().toLowerCase());
ignoreInfo.addAllForFile(codes);
}
return ignoreInfo;
}
}

View file

@ -195,4 +195,35 @@ const y = x; //CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
]);
}
test_ignore_for_file() async {
Source source = addSource('''
int x = ''; //INVALID_ASSIGNMENT
const y = x; //CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
// ignore_for_file: invalid_assignment
''');
await computeAnalysisResult(source);
assertErrors(source,
[CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE]);
}
test_multiple_ignore_for_files() async {
Source source = addSource('''
int x = ''; //INVALID_ASSIGNMENT
const y = x; //CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
// ignore_for_file: invalid_assignment,const_initialized_with_non_constant_value
''');
await computeAnalysisResult(source);
assertErrors(source, []);
}
test_ignore_for_file_whitespace_variant() async {
Source source = addSource('''
//ignore_for_file: const_initialized_with_non_constant_value , invalid_assignment
int x = ''; //INVALID_ASSIGNMENT
const y = x; //CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
''');
await computeAnalysisResult(source);
assertErrors(source, []);
}
}

View file

@ -4360,16 +4360,20 @@ class ScanDartTaskTest extends _AbstractDartTaskTest {
test_ignore_info() {
_performScanTask('''
//ignore: error_code
//ignore_for_file: error_code
var x = '';
foo(); // ignore: error_code_2
bar(); //ignore: error_code, error_code_2
// ignore_for_file: error_code_2, error_code_3
''');
IgnoreInfo info = outputs[IGNORE_INFO];
expect(info.ignores.keys, hasLength(3));
expect(info.ignores[1].first, 'error_code');
expect(info.ignores[3].first, 'error_code_2');
expect(info.ignores[4], unorderedEquals(['error_code', 'error_code_2']));
expect(info.ignores[4].first, 'error_code_2');
expect(info.ignores[5], unorderedEquals(['error_code', 'error_code_2']));
expect(info.ignoreForFiles,
unorderedEquals(['error_code', 'error_code_2', 'error_code_3']));
}
test_perform_errors() {