Skip to content

Granular report_exclude filtering by unused declaration kind #1012

@dafi

Description

@dafi

Problem Description

Currently, report_exclude only supports excluding entire files or directories, which excludes ALL types of unused declarations from the results. There is no way to selectively exclude specific kinds of unused declarations (e.g., unused function, unused property) while keeping others for the same files/directories.

This limitation makes it difficult to manage analysis results in projects with mixed requirements, where certain unused types should be ignored in specific directories, while others should still be reported.

Expected Behavior

I would like to be able to specify exclusion rules based on both path patterns AND unused declaration kinds, similar to how some linters allow rule-specific exclusions.

Proposed syntax in .periphery.yml:

report_exclude:
  # Exclude unused functions in SharedKit directory
  - path: "**/SharedKit/**"
    kinds:
      - unused.function
      - unused.function.parameter
  
  # Exclude all unused declarations in GeneratedCode
  - path: "**/GeneratedCode/**"
  
  # Alternative single-line syntax
  - "**/SharedKit/**:unused.function,unused.function.parameter"

With this configuration:

  • ✅ Unused properties in SharedKit/ would still be reported
  • ❌ Unused functions in SharedKit/ would be excluded from results
  • ❌ All unused declarations in GeneratedCode/ would be excluded

Current Behavior

Currently, the only way to exclude results from a directory consists to add granular comments periphery:ignore or:

report_exclude:
  - "**/SharedKit/**"

This excludes ALL types of unused declarations (functions, properties, classes, etc.) from the results, which is too aggressive when you only want to filter specific kinds.

Use Case Examples

Example 1: Shared Framework with Mixed Requirements

Consider a shared framework (SharedKit) that:

  • Contains utility functions that are intentionally unused in the main app but used by other modules
  • Has properties that should be validated for actual usage

Current situation:

// SharedKit/Utilities.swift
struct Utilities {
    static func helperFunction() { /* unused but needed for other modules */ }
    static var unusedProperty: String = "test" // Should be detected!
}

With current report_exclude:

report_exclude:
  - "**/SharedKit/**"

Result: Both helperFunction and unusedProperty are excluded from results ❌

Desired result with granular filtering:

report_exclude:
  - path: "**/SharedKit/**"
    kinds:
      - unused.function

Result:

  • helperFunction is excluded ✅
  • unusedProperty is still reported as unused ✅

Example 2: Code Pending Migration to External Library

Consider a directory containing code that will be extracted into a separate library but currently lives in the main project:

// FutureLibrary/NetworkingUtils.swift
// This will become a standalone library soon

struct NetworkingUtils {
    // Used by future library consumers, not by current project
    static func parseHeaders(_ headers: [String: String]) -> [Header] { /* ... */ }
    
    // Actually unused and should be cleaned up
    static var deprecatedEndpoint: String = "https://old.api.com"
}

Problem: These functions aren't used in the current project, but they will be needed once extracted into a library. However, truly unused code (like deprecatedEndpoint) should still be detected.

With current report_exclude:

report_exclude:
  - "**/FutureLibrary/**"

Result: Both parseHeaders and deprecatedEndpoint are excluded ❌

Desired result with granular filtering:

report_exclude:
  - path: "**/FutureLibrary/**"
    kinds:
      - unused.function
      - unused.function.parameter

Result:

  • parseHeaders is excluded (will be used in the library) ✅
  • deprecatedEndpoint is still reported as unused (can be cleaned up now) ✅

This allows teams to:

  • Keep unused functions that represent the future library's public API
  • Still detect and clean up unused properties, types, and other declarations before migration
  • Maintain code quality during refactoring phases

Workarounds Considered

  1. Comment commands (// periphery:ignore): Works but requires adding comments to every function we want to ignore, which doesn't scale well
  2. Post-processing with jq: Adds complexity to CI/CD pipelines
  3. index_exclude: Too aggressive, removes files entirely from analysis

None of these provide a clean, declarative configuration solution.

Additional Context

This feature would greatly improve Periphery's flexibility for projects with:

  • Shared frameworks with public APIs
  • Code being refactored/extracted into separate libraries
  • Generated code in specific directories
  • Mixed Objective-C/Swift codebases where certain patterns should be ignored
  • Monorepos with different analysis requirements per module

The proposed syntax is inspired by similar features in other linters (SwiftLint, ESLint) that allow rule-specific exclusions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions