Repository: astral-sh/ruff
File: crates/ruff/src/commands/analyze_graph.rs (Rust)

Code before:
    if paths.is_empty() {
        warn_user_once!("No Python files found under the given path(s)");
        return Ok(ExitStatus::Success);
    }

    // Resolve all package roots.
    let package_roots = resolver
        .package_roots(
            &paths
                .iter()
                .flatten()
                .map(ResolvedFile::path)
                .collect::<Vec<_>>(),
        )
        .into_iter()
        .map(|(path, package)| {
            (
                path.to_path_buf(),
                package.map(PackageRoot::path).map(Path::to_path_buf),
            )
        })
        .collect::<FxHashMap<_, _>>();

    // Create a database from the source roots, combining detected package roots with configured
    // `src` paths.
    let mut src_roots: Vec<SystemPathBuf> = package_roots
        .values()
        .filter_map(|package| package.as_deref())
        .filter_map(|package| package.parent())
        .map(Path::to_path_buf)
        .filter_map(|path| SystemPathBuf::from_path_buf(path).ok())
        .collect();

    // Add configured `src` paths, filtering to only include existing directories.
    src_roots.extend(
        pyproject_config
            .settings
            .linter
            .src
            .iter()
            .filter(|path| path.is_dir())
            .filter_map(|path| SystemPathBuf::from_path_buf(path.clone()).ok()),
    );

    let db = ModuleDb::from_src_roots(
        src_roots,
        pyproject_config
            .settings
            .analyze
            .target_version
            .as_tuple()

Review comment (refactor):
Should we use an `FxIndexSet` to avoid duplicate roots? I think I'd also expect that explicitly configured `src` roots take precedence over the automatically detected `package_roots` (which means, they have to come first).



Code after:

    if paths.is_empty() {
        warn_user_once!("No Python files found under the given path(s)");
        return Ok(ExitStatus::Success);
    }

    // Resolve all package roots.
    let package_roots = resolver
        .package_roots(
            &paths
                .iter()
                .flatten()
                .map(ResolvedFile::path)
                .collect::<Vec<_>>(),
        )
        .into_iter()
        .map(|(path, package)| {
            (
                path.to_path_buf(),
                package.map(PackageRoot::path).map(Path::to_path_buf),
            )
        })
        .collect::<FxHashMap<_, _>>();

    // Create a database from the source roots, combining configured `src` paths with detected
    // package roots. Configured paths are added first so they take precedence, and duplicates
    // are removed.
    let mut src_roots: IndexSet<SystemPathBuf, FxBuildHasher> = IndexSet::default();

    // Add configured `src` paths first (for precedence), filtering to only include existing
    // directories.
    src_roots.extend(
        pyproject_config
            .settings
            .linter
            .src
            .iter()
            .filter(|path| path.is_dir())
            .filter_map(|path| SystemPathBuf::from_path_buf(path.clone()).ok()),
    );

    // Add detected package roots.
    src_roots.extend(
        package_roots
            .values()
            .filter_map(|package| package.as_deref())
            .filter_map(|path| path.parent())
            .filter_map(|path| SystemPathBuf::from_path_buf(path.to_path_buf()).ok()),
    );

    let db = ModuleDb::from_src_roots(