| // Copyright (c) 2019, 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 'package:analyzer/analysis_rule/analysis_rule.dart'; |
| import 'package:analyzer/analysis_rule/rule_context.dart'; |
| import 'package:analyzer/analysis_rule/rule_visitor_registry.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/token.dart'; |
| import 'package:analyzer/dart/ast/visitor.dart'; |
| import 'package:analyzer/error/error.dart'; |
| |
| import '../analyzer.dart'; |
| import '../extensions.dart'; |
| |
| const _desc = "Don't use `final` for local variables."; |
| |
| class UnnecessaryFinal extends MultiAnalysisRule { |
| UnnecessaryFinal() |
| : super(name: LintNames.unnecessary_final, description: _desc); |
| |
| @override |
| List<DiagnosticCode> get diagnosticCodes => [ |
| LinterLintCode.unnecessaryFinalWithType, |
| LinterLintCode.unnecessaryFinalWithoutType, |
| ]; |
| |
| @override |
| List<String> get incompatibleRules => const [ |
| LintNames.prefer_final_locals, |
| LintNames.prefer_final_parameters, |
| LintNames.prefer_final_in_for_each, |
| ]; |
| |
| @override |
| void registerNodeProcessors( |
| RuleVisitorRegistry registry, |
| RuleContext context, |
| ) { |
| var visitor = _Visitor(this); |
| registry |
| ..addFormalParameterList(this, visitor) |
| ..addForStatement(this, visitor) |
| ..addDeclaredVariablePattern(this, visitor) |
| ..addVariableDeclarationStatement(this, visitor); |
| } |
| } |
| |
| class _Visitor extends SimpleAstVisitor<void> { |
| final MultiAnalysisRule rule; |
| |
| _Visitor(this.rule); |
| |
| LintCode getErrorCode(Object? type) => type == null |
| ? LinterLintCode.unnecessaryFinalWithoutType |
| : LinterLintCode.unnecessaryFinalWithType; |
| |
| (Token?, AstNode?) getParameterDetails(FormalParameter node) { |
| var parameter = node is DefaultFormalParameter ? node.parameter : node; |
| return switch (parameter) { |
| FieldFormalParameter() => (parameter.keyword, parameter.type), |
| SimpleFormalParameter() => (parameter.keyword, parameter.type), |
| SuperFormalParameter() => (parameter.keyword, parameter.type), |
| _ => (null, null), |
| }; |
| } |
| |
| @override |
| void visitDeclaredVariablePattern(DeclaredVariablePattern node) { |
| var keyword = node.keyword; |
| keyword ??= node |
| .thisOrAncestorOfType<PatternVariableDeclaration>() |
| ?.keyword; |
| if (keyword == null || keyword.type != Keyword.FINAL) return; |
| |
| var errorCode = getErrorCode(node.matchedValueType); |
| rule.reportAtToken(keyword, diagnosticCode: errorCode); |
| } |
| |
| @override |
| void visitFormalParameterList(FormalParameterList parameterList) { |
| for (var node in parameterList.parameters) { |
| if (node.isFinal) { |
| var (keyword, type) = getParameterDetails(node); |
| if (keyword == null) continue; |
| |
| var errorCode = getErrorCode(type); |
| rule.reportAtToken(keyword, diagnosticCode: errorCode); |
| } |
| } |
| } |
| |
| @override |
| void visitForStatement(ForStatement node) { |
| var forLoopParts = node.forLoopParts; |
| // If the following `if` test fails, then either the statement is not a |
| // for-each loop, or it is something like `for(a in b) { ... }`. In the |
| // second case, notice `a` is not actually declared from within the |
| // loop. `a` is a variable declared outside the loop. |
| if (forLoopParts is ForEachPartsWithDeclaration) { |
| var loopVariable = forLoopParts.loopVariable; |
| var keyword = loopVariable.keyword; |
| if (keyword == null) return; |
| if (loopVariable.isFinal) { |
| var errorCode = getErrorCode(loopVariable.type); |
| rule.reportAtToken(keyword, diagnosticCode: errorCode); |
| } |
| } else if (forLoopParts is ForEachPartsWithPattern) { |
| var keyword = forLoopParts.keyword; |
| if (keyword.isFinal) { |
| rule.reportAtToken( |
| keyword, |
| diagnosticCode: LinterLintCode.unnecessaryFinalWithoutType, |
| ); |
| } |
| } |
| } |
| |
| @override |
| void visitVariableDeclarationStatement(VariableDeclarationStatement node) { |
| var keyword = node.variables.keyword; |
| if (keyword == null) return; |
| if (node.variables.isFinal) { |
| var errorCode = getErrorCode(node.variables.type); |
| rule.reportAtToken(keyword, diagnosticCode: errorCode); |
| } |
| } |
| } |