Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unused_import: false rule violation (importing SwiftUI to use ObservableObject and @Published in a macOS project) #5167

Open
2 tasks done
grantneufeld opened this issue Aug 11, 2023 · 3 comments
Labels
bug Unexpected and reproducible misbehavior.

Comments

@grantneufeld
Copy link

New Issue Checklist

Describe the bug

Multi-target (iOS and macOS) project, when building for macOS (platform is important for my example: the code is a replacement for the EditMode SwiftUI provides on iOS but not on macOS), I have a file that imports SwiftUI to be able to specify that a class conforms to the ObservableObject protocol, and has a @Published property.

The unused_import rule is incorrectly reporting that import call as unused. If I remove the import line, I get errors in Xcode:

  • Cannot find type 'ObservableObject' in scope
  • Unknown attribute 'Published'

So, clearly, the compiler uses the import.

Complete output when running SwiftLint, including the stack trace and command used

Xcode Build:

$ xcodebuild -project WebExtractor.xcodeproj -scheme WebExtractor -destination platform=macOS,arch=x86_64 > xcodebuild.log

Outputs:

2023-08-10 20:06:15.685 xcodebuild[73360:7531983] DVTCoreDeviceEnabledState: DVTCoreDeviceEnabledState_Disabled set via user default (DVTEnableCoreDevice=disabled)

The SwiftLint Analyse:

$ swiftlint analyze --compiler-log-path xcodebuild.log

Outputs:

Analyzing Swift files in current working directory
... (many Collecting lines omitted) ...
... (many Analyzing lines omitted) ...
Analyzing 'EditMode.swift' (9/14)
/Users/user/Projects/MyApp/MyApp/Views/EditMode.swift:1:1: warning: Unused Import Violation: All imported modules should be required to make the file compile (unused_import)
... (more Analyzing lines omitted) ...
Done analyzing! Found 1 violation, 0 serious in 41 files.

Environment

  • SwiftLint version 0.52.4
  • Installation method used: Homebrew
  • Paste your configuration file:
# Here’s the whole thing, even though just the analyzer_rules: unused_import matters for this report
disabled_rules:
  - for_where
  - force_cast
opt_in_rules:
  - accessibility_label_for_image
  - accessibility_trait_for_button
  - anonymous_argument_in_multiline_closure
  - array_init
  - attributes
  - balanced_xctest_lifecycle
  - closure_body_length
  - closure_end_indentation
  - closure_spacing
  - collection_alignment
  - comma_inheritance
  - conditional_returns_on_newline
  - contains_over_filter_count
  - contains_over_filter_is_empty
  - contains_over_first_not_nil
  - contains_over_range_nil_comparison
  - convenience_type
  - direct_return
  - discarded_notification_center_observer
  - discouraged_assert
  - discouraged_none_name
  - discouraged_object_literal
  - discouraged_optional_boolean
  - discouraged_optional_collection
  - empty_collection_literal
  - empty_count
  - empty_string
  - empty_xctest_method
  - enum_case_associated_values_count
  - expiring_todo
  - explicit_init
  - fallthrough
  - fatal_error_message
  - file_header
  - file_name
  - file_name_no_space
  - file_types_order
  - first_where
  - flatmap_over_map_reduce
  - function_default_parameter_at_end
  - ibinspectable_in_extension
  - identical_operands
  - implicit_return
  - implicitly_unwrapped_optional
  - indentation_width
  - joined_default_parameter
  - last_where
  - legacy_multiple
  - legacy_objc_type
  - legacy_random
  - let_var_whitespace
  - literal_expression_end_indentation
  - local_doc_comment
  - lower_acl_than_parent
  - missing_docs
  - modifier_order
  - multiline_arguments
  - multiline_arguments_brackets
  - multiline_function_chains
  - multiline_literal_brackets
  - multiline_parameters
  - multiline_parameters_brackets
  - nimble_operator
  - no_extension_access_modifier
  - no_magic_numbers
  - nslocalizedstring_key
  - nslocalizedstring_require_bundle
  - number_separator
  - object_literal
  - operator_usage_whitespace
  - optional_enum_case_matching
  - overridden_super_call
  - override_in_extension
  - pattern_matching_keywords
  - period_spacing
  - prefer_self_in_static_references
  - prefer_self_type_over_type_of_self
  - prefer_zero_over_explicit_init
  - private_action
  - private_subject
  - prohibited_super_call
  - quick_discouraged_call
  - quick_discouraged_focused_test
  - quick_discouraged_pending_test
  - raw_value_for_camel_cased_codable_enum
  - reduce_into
  - redundant_nil_coalescing
  - redundant_type_annotation
  - required_enum_case
  - return_value_from_void_function
  - self_binding
  - shorthand_optional_binding
  - single_test_class
  - sorted_enum_cases
  - sorted_first_last
  - sorted_imports
  - static_operator
  - strict_fileprivate
  - strong_iboutlet
  - superfluous_else
  - switch_case_on_newline
  - test_case_accessibility
  - toggle_bool
  - trailing_closure
  - unavailable_function
  - unhandled_throwing_task
  - unneeded_parentheses_in_closure_argument
  - unowned_variable_capture
  - untyped_error_in_catch
  - vertical_parameter_alignment_on_call
  - vertical_whitespace_between_cases
  - vertical_whitespace_closing_braces
  - vertical_whitespace_opening_braces
  - weak_delegate
  - xct_specific_matcher
  - yoda_condition

analyzer_rules:
  - capture_variable
  - explicit_self
  - typesafe_array_init
  - unused_import

attributes:
  always_on_same_line:
    - "@Environment"
    - "@IBAction"
    - "@NSManaged"
identifier_name:
  excluded:
    - id
    - to
type_name:
  excluded:
    - ID
  • Xcode 14.3.1
  • macOS 13.5 (Intel)
  • Do you have a sample that shows the issue?
// EditMode.swift
import SwiftUI

final class EditMode: ObservableObject {
    @Published var isEditing = false
}

(I normally have the content of that file wrapped in #if os(macOS)#endif compiler directives, but trimmed that out and built just for the macOS destination to see if that would change anything, but the incorrect rule warning came out the same.)

@grantneufeld
Copy link
Author

Update:

If the import SwiftUI line is removed from the sample failing file above, two errors show up in Xcode:

// removed:
// import SwiftUI

/// Since EditMode only exists for iOS, here’s a simplified version for macOS.
final class EditMode: ObservableObject { // 🛑 Cannot find type 'ObservableObject' in scope
    /// True if the view is in “edit” mode.
    @Published var isEditing = false // 🛑 Unknown attribute 'Published'
}

If I instead swap in Foundation for SwiftUI, then the errors go away, and SwiftLint Analyze does not incorrectly report an unused_import violation.

Weird.

Even though import SwiftUI makes the code work, the rule is considered violated when I use that import instead of import Foundation. 🤷

@grantneufeld
Copy link
Author

More data for this:

import Foundation
enum A11y {
    static let min: CGFloat = 44
}

The above example triggers the false rule violation. (CGFloat is not an available type unless defined by an import).

Substituting import CoreGraphics for the import Foundation line also produces the false rule violation.

But substituting import CoreFoundation.CFNumber does not trigger the rule violation.

So it seems that the unused_import rule is triggering anytime the import is needed, but could be replaced with a more specific import. Weird.

@SimplyDanny SimplyDanny added the bug Unexpected and reproducible misbehavior. label Jan 23, 2024
@PaulTaykalo
Copy link
Collaborator

Right, so what is happening is this:

  • swiftlint is asking for a cursorInfo for ObservableObject token
  • sourcekit replies that ObservableObject is actually Combine.ObservableObject from Combine module.
  • swiftlint then decides that no objects were references from SwiftUI and removes it from imports

The issue is that ObservableObject is actually was imported throught SwiftUI.

So suggestion is to have something like allowed_transitive rules, which will check if we actually using one of the objects and inside the re-exported module.

@ConfigurationElement(key: "allowed_transitive_imports")

OR:

As an alternative, we could use the same configuration for detecting such issues, and not remove imports that are actually used like Umbrella import :)

#5622 as a draft

@jpsim @SimplyDanny

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Unexpected and reproducible misbehavior.
Projects
None yet
Development

No branches or pull requests

3 participants