runAnalysis method

Future<void> runAnalysis(
  1. Iterable<String> folders,
  2. String rootFolder
)

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),
          );
        }
      });
    }
  }
}