runAnalysis method
Return a future that will complete after static analysis done for files from folders
.
Implementation
Future<void> runAnalysis(Iterable<String> folders, String rootFolder) async {
final collection = AnalysisContextCollection(
includedPaths:
folders.map((path) => p.normalize(p.join(rootFolder, path))).toList(),
resourceProvider: PhysicalResourceProvider.INSTANCE,
);
final filePaths = folders
.expand((directory) => Glob('$directory/**.dart')
.listFileSystemSync(
const LocalFileSystem(),
root: rootFolder,
followLinks: false,
)
.whereType<File>()
.where((entity) => !_isExcluded(
p.relative(entity.path, from: rootFolder),
_globalExclude,
))
.map((entity) => entity.path))
.toSet();
for (final context in collection.contexts) {
final analyzedFiles =
filePaths.intersection(context.contextRoot.analyzedFiles().toSet());
for (final filePath in analyzedFiles) {
// ignore: deprecated_member_use
final result = await context.currentSession.getResolvedUnit(filePath);
final unit = result.unit;
final content = result.content;
if (unit == null ||
content == null ||
result.state != ResultState.VALID) {
continue;
}
final internalResult = InternalResolvedUnitResult(
result.uri,
content,
unit,
result.lineInfo,
);
final visitor = ScopeVisitor();
internalResult.unit.visitChildren(visitor);
final functions = visitor.functions.where((function) {
final declaration = function.declaration;
if (declaration is ConstructorDeclaration &&
declaration.body is EmptyFunctionBody) {
return false;
} else if (declaration is MethodDeclaration &&
declaration.body is EmptyFunctionBody) {
return false;
}
return true;
}).toList();
final lineInfo = internalResult.lineInfo;
_store.recordFile(filePath, rootFolder, (builder) {
if (!_isExcluded(
p.relative(filePath, from: rootFolder),
_metricsExclude,
)) {
for (final classDeclaration in visitor.classes) {
builder.recordClass(
classDeclaration,
Report(
location: nodeLocation(
node: classDeclaration.declaration,
source: internalResult,
),
metrics: [
for (final metric in _classesMetrics)
if (metric.supports(
classDeclaration.declaration,
visitor.classes,
visitor.functions,
internalResult,
))
metric.compute(
classDeclaration.declaration,
visitor.classes,
visitor.functions,
internalResult,
),
],
),
);
}
for (final function in functions) {
final cyclomatic = _methodsMetrics
.firstWhere(
(metric) =>
metric.id == CyclomaticComplexityMetric.metricId,
)
.compute(
function.declaration,
visitor.classes,
visitor.functions,
internalResult,
);
final linesOfExecutableCodeVisitor = SourceCodeVisitor(lineInfo);
function.declaration.visitChildren(linesOfExecutableCodeVisitor);
final linesOfExecutableCode = MetricValue<int>(
metricsId: linesOfExecutableCodeKey,
documentation: const MetricDocumentation(
name: '',
shortName: '',
brief: '',
measuredType: EntityType.methodEntity,
examples: [],
),
value: linesOfExecutableCodeVisitor.linesWithCode.length,
level: valueLevel(
linesOfExecutableCodeVisitor.linesWithCode.length,
readThreshold<int>(
_metricsConfig,
linesOfExecutableCodeKey,
linesOfExecutableCodeDefaultWarningLevel,
),
),
comment: '',
);
final halsteadVolumeAstVisitor = HalsteadVolumeAstVisitor();
function.declaration.visitChildren(halsteadVolumeAstVisitor);
// Total number of occurrences of operators.
final totalNumberOfOccurrencesOfOperators =
sum(halsteadVolumeAstVisitor.operators.values);
// Total number of occurrences of operands
final totalNumberOfOccurrencesOfOperands =
sum(halsteadVolumeAstVisitor.operands.values);
// Number of distinct operators.
final numberOfDistinctOperators =
halsteadVolumeAstVisitor.operators.keys.length;
// Number of distinct operands.
final numberOfDistinctOperands =
halsteadVolumeAstVisitor.operands.keys.length;
// Halstead Program Length – The total number of operator occurrences and the total number of operand occurrences.
final halsteadProgramLength =
totalNumberOfOccurrencesOfOperators +
totalNumberOfOccurrencesOfOperands;
// Halstead Vocabulary – The total number of unique operator and unique operand occurrences.
final halsteadVocabulary =
numberOfDistinctOperators + numberOfDistinctOperands;
// Program Volume – Proportional to program size, represents the size, in bits, of space necessary for storing the program. This parameter is dependent on specific algorithm implementation.
final halsteadVolume =
halsteadProgramLength * log2(max(1, halsteadVocabulary));
final maintainabilityIndex = max(
0,
(171 -
5.2 * log(max(1, halsteadVolume)) -
cyclomatic.value * 0.23 -
16.2 * log(max(1, linesOfExecutableCode.value))) *
100 /
171,
).toDouble();
builder.recordFunctionData(
function,
Report(
location: nodeLocation(
node: function.declaration,
source: internalResult,
),
metrics: [
for (final metric in _methodsMetrics)
if (metric.supports(
function.declaration,
visitor.classes,
visitor.functions,
internalResult,
))
metric.compute(
function.declaration,
visitor.classes,
visitor.functions,
internalResult,
),
MetricValue<double>(
metricsId: 'maintainability-index',
documentation: const MetricDocumentation(
name: '',
shortName: '',
brief: '',
measuredType: EntityType.classEntity,
examples: [],
),
value: maintainabilityIndex,
level: _maintainabilityIndexViolationLevel(
maintainabilityIndex,
),
comment: '',
),
if (_metricsConfig.containsKey(linesOfExecutableCodeKey))
linesOfExecutableCode,
],
),
);
}
}
final ignores = Suppression(internalResult.content, lineInfo);
builder.recordIssues(_checkOnCodeIssues(
ignores,
internalResult,
filePath,
rootFolder,
));
if (!_isExcluded(
p.relative(filePath, from: rootFolder),
_metricsExclude,
)) {
builder.recordAntiPatternCases(
_checkOnAntiPatterns(ignores, internalResult, functions),
);
}
});
}
}
}