This repository has been archived by the owner on Jul 16, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: introduce prefer-provide-intl-description rule (#1137)
* feat: introduce prefer-provide-intl-description rule * chore: fixes after review
- Loading branch information
1 parent
592cd3d
commit 29e6181
Showing
9 changed files
with
463 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
...ules/rules_list/prefer_provide_intl_description/prefer_provide_intl_description_rule.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// ignore_for_file: public_member_api_docs | ||
|
||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer/dart/ast/visitor.dart'; | ||
|
||
import '../../../../../utils/node_utils.dart'; | ||
import '../../../lint_utils.dart'; | ||
import '../../../models/internal_resolved_unit_result.dart'; | ||
import '../../../models/issue.dart'; | ||
import '../../../models/severity.dart'; | ||
import '../../models/intl_rule.dart'; | ||
import '../../rule_utils.dart'; | ||
|
||
part 'visitor.dart'; | ||
|
||
class PreferProvideIntlDescriptionRule extends IntlRule { | ||
static const String ruleId = 'prefer-provide-intl-description'; | ||
|
||
static const _warning = 'Provide description for translated message'; | ||
|
||
PreferProvideIntlDescriptionRule([Map<String, Object> config = const {}]) | ||
: super( | ||
id: ruleId, | ||
severity: readSeverity(config, Severity.warning), | ||
excludes: readExcludes(config), | ||
includes: readIncludes(config), | ||
); | ||
|
||
@override | ||
Iterable<Issue> check(InternalResolvedUnitResult source) { | ||
final visitor = _Visitor(); | ||
|
||
source.unit.visitChildren(visitor); | ||
|
||
return visitor.declarations | ||
.map((declaration) => createIssue( | ||
rule: this, | ||
location: nodeLocation( | ||
node: declaration, | ||
source: source, | ||
), | ||
message: _warning, | ||
)) | ||
.toList(growable: false); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
...src/analyzers/lint_analyzer/rules/rules_list/prefer_provide_intl_description/visitor.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
part of 'prefer_provide_intl_description_rule.dart'; | ||
|
||
class _Visitor extends RecursiveAstVisitor<void> { | ||
static const _supportedMethods = {'message', 'plural', 'gender', 'select'}; | ||
|
||
final _declarations = <MethodInvocation>[]; | ||
|
||
Iterable<MethodInvocation> get declarations => _declarations; | ||
|
||
@override | ||
void visitMethodInvocation(MethodInvocation node) { | ||
super.visitMethodInvocation(node); | ||
|
||
final target = node.realTarget; | ||
if (target != null && | ||
target is SimpleIdentifier && | ||
target.name == 'Intl' && | ||
_supportedMethods.contains(node.methodName.name) && | ||
_withEmptyDescription(node.argumentList)) { | ||
_declarations.add(node); | ||
} | ||
} | ||
|
||
bool _withEmptyDescription(ArgumentList args) => | ||
args.arguments.any((argument) => | ||
argument is NamedExpression && | ||
argument.name.label.name == 'desc' && | ||
argument.expression is SimpleStringLiteral && | ||
(argument.expression as SimpleStringLiteral).value.isEmpty) || | ||
args.arguments.every((argument) => | ||
argument is! NamedExpression || argument.name.label.name != 'desc'); | ||
} |
83 changes: 83 additions & 0 deletions
83
...zers/lint_analyzer/rules/rules_list/prefer_provide_intl_description/examples/example.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
class SomeClassI18n { | ||
static final String message = Intl.message( | ||
'message', | ||
name: 'SomeClassI18n_message', | ||
desc: 'Message description', | ||
); | ||
|
||
static String plural = Intl.plural( | ||
1, | ||
one: 'one', | ||
other: 'other', | ||
name: 'SomeClassI18n_plural', | ||
desc: 'Plural description', | ||
); | ||
|
||
static String gender = Intl.gender( | ||
'other', | ||
female: 'female', | ||
male: 'male', | ||
other: 'other', | ||
name: 'SomeClassI18n_gender', | ||
desc: 'Gender description', | ||
); | ||
|
||
static String select = Intl.select( | ||
true, | ||
{true: 'true', false: 'false'}, | ||
name: 'SomeClassI18n_select', | ||
desc: 'Select description', | ||
); | ||
} | ||
|
||
class Intl { | ||
static String message(String messageText, | ||
{String? desc = '', | ||
Map<String, Object>? examples, | ||
String? locale, | ||
String? name, | ||
List<Object>? args, | ||
String? meaning, | ||
bool? skip}) => | ||
''; | ||
|
||
static String plural(num howMany, | ||
{String? zero, | ||
String? one, | ||
String? two, | ||
String? few, | ||
String? many, | ||
required String other, | ||
String? desc, | ||
Map<String, Object>? examples, | ||
String? locale, | ||
int? precision, | ||
String? name, | ||
List<Object>? args, | ||
String? meaning, | ||
bool? skip}) => | ||
''; | ||
|
||
static String gender(String targetGender, | ||
{String? female, | ||
String? male, | ||
required String other, | ||
String? desc, | ||
Map<String, Object>? examples, | ||
String? locale, | ||
String? name, | ||
List<Object>? args, | ||
String? meaning, | ||
bool? skip}) => | ||
''; | ||
|
||
static String select(Object choice, Map<Object, String> cases, | ||
{String? desc, | ||
Map<String, Object>? examples, | ||
String? locale, | ||
String? name, | ||
List<Object>? args, | ||
String? meaning, | ||
bool? skip}) => | ||
''; | ||
} |
111 changes: 111 additions & 0 deletions
111
...analyzer/rules/rules_list/prefer_provide_intl_description/examples/incorrect_example.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
class SomeClassI18n { | ||
static final String message = Intl.message( | ||
'message', | ||
name: 'SomeClassI18n_message', | ||
desc: '', | ||
); | ||
|
||
static final String message2 = Intl.message( | ||
'message2', | ||
name: 'SomeClassI18n_message2', | ||
); | ||
|
||
static String plural = Intl.plural( | ||
1, | ||
one: 'one', | ||
other: 'other', | ||
name: 'SomeClassI18n_plural', | ||
desc: '', | ||
); | ||
|
||
static String plural2 = Intl.plural( | ||
2, | ||
one: 'one', | ||
other: 'other', | ||
name: 'SomeClassI18n_plural2', | ||
); | ||
|
||
static String gender = Intl.gender( | ||
'other', | ||
female: 'female', | ||
male: 'male', | ||
other: 'other', | ||
name: 'SomeClassI18n_gender', | ||
desc: '', | ||
); | ||
|
||
static String gender2 = Intl.gender( | ||
'other', | ||
female: 'female', | ||
male: 'male', | ||
other: 'other', | ||
name: 'SomeClassI18n_gender2', | ||
); | ||
|
||
static String select = Intl.select( | ||
true, | ||
{true: 'true', false: 'false'}, | ||
name: 'SomeClassI18n_select', | ||
desc: '', | ||
); | ||
|
||
static String select2 = Intl.select( | ||
false, | ||
{true: 'true', false: 'false'}, | ||
name: 'SomeClassI18n_select', | ||
); | ||
} | ||
|
||
class Intl { | ||
Intl(); | ||
|
||
static String message(String messageText, | ||
{String? desc = '', | ||
Map<String, Object>? examples, | ||
String? locale, | ||
String? name, | ||
List<Object>? args, | ||
String? meaning, | ||
bool? skip}) => | ||
''; | ||
|
||
static String plural(num howMany, | ||
{String? zero, | ||
String? one, | ||
String? two, | ||
String? few, | ||
String? many, | ||
required String other, | ||
String? desc, | ||
Map<String, Object>? examples, | ||
String? locale, | ||
int? precision, | ||
String? name, | ||
List<Object>? args, | ||
String? meaning, | ||
bool? skip}) => | ||
''; | ||
|
||
static String gender(String targetGender, | ||
{String? female, | ||
String? male, | ||
required String other, | ||
String? desc, | ||
Map<String, Object>? examples, | ||
String? locale, | ||
String? name, | ||
List<Object>? args, | ||
String? meaning, | ||
bool? skip}) => | ||
''; | ||
|
||
static String select(Object choice, Map<Object, String> cases, | ||
{String? desc, | ||
Map<String, Object>? examples, | ||
String? locale, | ||
String? name, | ||
List<Object>? args, | ||
String? meaning, | ||
bool? skip}) => | ||
''; | ||
} |
96 changes: 96 additions & 0 deletions
96
...rules_list/prefer_provide_intl_description/prefer_provide_intl_description_rule_test.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/models/severity.dart'; | ||
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/rules/rules_list/prefer_provide_intl_description/prefer_provide_intl_description_rule.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
import '../../../../../helpers/rule_test_helper.dart'; | ||
|
||
const _examplePath = 'prefer_provide_intl_description/examples/example.dart'; | ||
const _incorrectExamplePath = | ||
'prefer_provide_intl_description/examples/incorrect_example.dart'; | ||
|
||
void main() { | ||
group('$PreferProvideIntlDescriptionRule', () { | ||
test('initialization', () async { | ||
final unit = await RuleTestHelper.resolveFromFile(_incorrectExamplePath); | ||
final issues = PreferProvideIntlDescriptionRule().check(unit); | ||
|
||
RuleTestHelper.verifyInitialization( | ||
issues: issues, | ||
ruleId: 'prefer-provide-intl-description', | ||
severity: Severity.warning, | ||
); | ||
}); | ||
|
||
test('reports no issues', () async { | ||
final unit = await RuleTestHelper.resolveFromFile(_examplePath); | ||
final issues = PreferProvideIntlDescriptionRule().check(unit); | ||
|
||
RuleTestHelper.verifyNoIssues(issues); | ||
}); | ||
|
||
test('reports about found issues for incorrect names', () async { | ||
final unit = await RuleTestHelper.resolveFromFile(_incorrectExamplePath); | ||
final issues = PreferProvideIntlDescriptionRule().check(unit); | ||
|
||
RuleTestHelper.verifyIssues( | ||
issues: issues, | ||
startLines: [2, 8, 13, 21, 28, 37, 45, 52], | ||
startColumns: [33, 34, 26, 27, 26, 27, 26, 27], | ||
locationTexts: [ | ||
'Intl.message(\n' | ||
" 'message',\n" | ||
" name: 'SomeClassI18n_message',\n" | ||
" desc: '',\n" | ||
' )', | ||
'Intl.message(\n' | ||
" 'message2',\n" | ||
" name: 'SomeClassI18n_message2',\n" | ||
' )', | ||
'Intl.plural(\n' | ||
' 1,\n' | ||
" one: 'one',\n" | ||
" other: 'other',\n" | ||
" name: 'SomeClassI18n_plural',\n" | ||
" desc: '',\n" | ||
' )', | ||
'Intl.plural(\n' | ||
' 2,\n' | ||
" one: 'one',\n" | ||
" other: 'other',\n" | ||
" name: 'SomeClassI18n_plural2',\n" | ||
' )', | ||
'Intl.gender(\n' | ||
" 'other',\n" | ||
" female: 'female',\n" | ||
" male: 'male',\n" | ||
" other: 'other',\n" | ||
" name: 'SomeClassI18n_gender',\n" | ||
" desc: '',\n" | ||
' )', | ||
'Intl.gender(\n' | ||
" 'other',\n" | ||
" female: 'female',\n" | ||
" male: 'male',\n" | ||
" other: 'other',\n" | ||
" name: 'SomeClassI18n_gender2',\n" | ||
' )', | ||
'Intl.select(\n' | ||
' true,\n' | ||
" {true: 'true', false: 'false'},\n" | ||
" name: 'SomeClassI18n_select',\n" | ||
" desc: '',\n" | ||
' )', | ||
'Intl.select(\n' | ||
' false,\n' | ||
" {true: 'true', false: 'false'},\n" | ||
" name: 'SomeClassI18n_select',\n" | ||
' )', | ||
], | ||
messages: List.filled( | ||
issues.length, | ||
'Provide description for translated message', | ||
), | ||
); | ||
}); | ||
}); | ||
} |
Oops, something went wrong.