diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 39ba815cfca..f9a5ef46786 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -5,7 +5,7 @@ class Projects::BlobController < Projects::ApplicationController include ActionView::Helpers::SanitizeHelper # Raised when given an invalid file path - class InvalidPathError < StandardError; end + InvalidPathError = Class.new(StandardError) before_action :require_non_empty_project, except: [:new, :create] before_action :authorize_download_code! diff --git a/app/models/project.rb b/app/models/project.rb index e06fc30dc8a..0c2494d3c32 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -19,7 +19,7 @@ class Project < ActiveRecord::Base extend Gitlab::ConfigHelper - class BoardLimitExceeded < StandardError; end + BoardLimitExceeded = Class.new(StandardError) NUMBER_OF_PERMITTED_BOARDS = 1 UNKNOWN_IMPORT_URL = 'http://unknown.git'.freeze diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 9891f5edf41..539b31780b3 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -7,7 +7,7 @@ class ProjectWiki 'AsciiDoc' => :asciidoc }.freeze unless defined?(MARKUPS) - class CouldNotCreateWikiError < StandardError; end + CouldNotCreateWikiError = Class.new(StandardError) # Returns a string describing what went wrong after # an operation fails. diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb index 2bc6cf3266e..ab2d3d5a3ec 100644 --- a/app/serializers/pipeline_serializer.rb +++ b/app/serializers/pipeline_serializer.rb @@ -1,5 +1,5 @@ class PipelineSerializer < BaseSerializer - class InvalidResourceError < StandardError; end + InvalidResourceError = Class.new(StandardError) entity PipelineEntity diff --git a/app/services/commits/change_service.rb b/app/services/commits/change_service.rb index 25e22f14e60..8a9bcd2d053 100644 --- a/app/services/commits/change_service.rb +++ b/app/services/commits/change_service.rb @@ -1,7 +1,7 @@ module Commits class ChangeService < ::BaseService - class ValidationError < StandardError; end - class ChangeError < StandardError; end + ValidationError = Class.new(StandardError) + ChangeError = Class.new(StandardError) def execute @start_project = params[:start_project] || @project diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb index 0a25f56d24c..31869c2f01e 100644 --- a/app/services/files/base_service.rb +++ b/app/services/files/base_service.rb @@ -1,6 +1,6 @@ module Files class BaseService < ::BaseService - class ValidationError < StandardError; end + ValidationError = Class.new(StandardError) def execute @start_project = params[:start_project] || @project diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb index 0609c6219e7..700f9f4f6f0 100644 --- a/app/services/files/multi_service.rb +++ b/app/services/files/multi_service.rb @@ -1,6 +1,6 @@ module Files class MultiService < Files::BaseService - class FileChangedError < StandardError; end + FileChangedError = Class.new(StandardError) ACTIONS = %w[create update delete move].freeze diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb index 54e1aaf3f67..fbbab97632e 100644 --- a/app/services/files/update_service.rb +++ b/app/services/files/update_service.rb @@ -1,6 +1,6 @@ module Files class UpdateService < Files::BaseService - class FileChangedError < StandardError; end + FileChangedError = Class.new(StandardError) def commit repository.update_file(current_user, @file_path, @file_content, diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb index a2a5f57d069..711f4035c55 100644 --- a/app/services/issues/move_service.rb +++ b/app/services/issues/move_service.rb @@ -1,6 +1,6 @@ module Issues class MoveService < Issues::BaseService - class MoveError < StandardError; end + MoveError = Class.new(StandardError) def execute(issue, new_project) @old_issue = issue diff --git a/app/services/merge_requests/resolve_service.rb b/app/services/merge_requests/resolve_service.rb index d22a1d3e0ad..82cd89d9a0b 100644 --- a/app/services/merge_requests/resolve_service.rb +++ b/app/services/merge_requests/resolve_service.rb @@ -1,7 +1,6 @@ module MergeRequests class ResolveService < MergeRequests::BaseService - class MissingFiles < Gitlab::Conflict::ResolutionError - end + MissingFiles = Class.new(Gitlab::Conflict::ResolutionError) attr_accessor :conflicts, :rugged, :merge_index, :merge_request diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 2e06826c311..a7142d5950e 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -2,7 +2,7 @@ module Projects class DestroyService < BaseService include Gitlab::ShellAdapter - class DestroyError < StandardError; end + DestroyError = Class.new(StandardError) DELETED_FLAG = '+deleted'.freeze diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb index cd230528743..1c5a549feb9 100644 --- a/app/services/projects/import_service.rb +++ b/app/services/projects/import_service.rb @@ -2,7 +2,7 @@ module Projects class ImportService < BaseService include Gitlab::ShellAdapter - class Error < StandardError; end + Error = Class.new(StandardError) def execute add_repository_to_project unless project.gitlab_project_import? diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 20dfbddc823..da6e6acd4a7 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -9,7 +9,7 @@ module Projects class TransferService < BaseService include Gitlab::ShellAdapter - class TransferError < StandardError; end + TransferError = Class.new(StandardError) def execute(new_namespace) if allowed_transfer?(current_user, project, new_namespace) diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb index c11f8529183..409cb5b924f 100644 --- a/lib/api/api_guard.rb +++ b/lib/api/api_guard.rb @@ -160,13 +160,10 @@ module API # Exceptions # - class MissingTokenError < StandardError; end - - class TokenNotFoundError < StandardError; end - - class ExpiredError < StandardError; end - - class RevokedError < StandardError; end + MissingTokenError = Class.new(StandardError) + TokenNotFoundError = Class.new(StandardError) + ExpiredError = Class.new(StandardError) + RevokedError = Class.new(StandardError) class InsufficientScopeError < StandardError attr_reader :scopes diff --git a/lib/bitbucket/error/unauthorized.rb b/lib/bitbucket/error/unauthorized.rb index 5e2eb57bb0e..efe10542f19 100644 --- a/lib/bitbucket/error/unauthorized.rb +++ b/lib/bitbucket/error/unauthorized.rb @@ -1,6 +1,5 @@ module Bitbucket module Error - class Unauthorized < StandardError - end + Unauthorized = Class.new(StandardError) end end diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 649ee4d018b..e390919ae1d 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -1,6 +1,6 @@ module Ci class GitlabCiYamlProcessor - class ValidationError < StandardError; end + ValidationError = Class.new(StandardError) include Gitlab::Ci::Config::Entry::LegacyValidationHelpers diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb index 9ece84cc469..dd864eea3fa 100644 --- a/lib/extracts_path.rb +++ b/lib/extracts_path.rb @@ -2,7 +2,7 @@ # file path string when combined in a request parameter module ExtractsPath # Raised when given an invalid file path - class InvalidPathError < StandardError; end + InvalidPathError = Class.new(StandardError) # Given a string containing both a Git tree-ish, such as a branch or tag, and # a filesystem path joined by forward slashes, attempts to separate the two. diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb index 3b210eeda9d..8c28009b9c6 100644 --- a/lib/gitlab/access.rb +++ b/lib/gitlab/access.rb @@ -5,7 +5,7 @@ # module Gitlab module Access - class AccessDeniedError < StandardError; end + AccessDeniedError = Class.new(StandardError) NO_ACCESS = 0 GUEST = 10 diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index 89db6c3da46..0a5abc92190 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -1,6 +1,6 @@ module Gitlab module Auth - class MissingPersonalTokenError < StandardError; end + MissingPersonalTokenError = Class.new(StandardError) SCOPES = [:api, :read_user].freeze DEFAULT_SCOPES = [:api].freeze diff --git a/lib/gitlab/ci/build/artifacts/metadata.rb b/lib/gitlab/ci/build/artifacts/metadata.rb index cd2e83b4c27..a375ccbece0 100644 --- a/lib/gitlab/ci/build/artifacts/metadata.rb +++ b/lib/gitlab/ci/build/artifacts/metadata.rb @@ -6,7 +6,7 @@ module Gitlab module Build module Artifacts class Metadata - class ParserError < StandardError; end + ParserError = Class.new(StandardError) VERSION_PATTERN = /^[\w\s]+(\d+\.\d+\.\d+)/ INVALID_PATH_PATTERN = %r{(^\.?\.?/)|(/\.?\.?/)} diff --git a/lib/gitlab/ci/config/entry/factory.rb b/lib/gitlab/ci/config/entry/factory.rb index 9f5e393d191..6be8288748f 100644 --- a/lib/gitlab/ci/config/entry/factory.rb +++ b/lib/gitlab/ci/config/entry/factory.rb @@ -6,7 +6,7 @@ module Gitlab # Factory class responsible for fabricating entry objects. # class Factory - class InvalidFactory < StandardError; end + InvalidFactory = Class.new(StandardError) def initialize(entry) @entry = entry diff --git a/lib/gitlab/ci/config/entry/node.rb b/lib/gitlab/ci/config/entry/node.rb index 5eef2868cd6..55a5447ab51 100644 --- a/lib/gitlab/ci/config/entry/node.rb +++ b/lib/gitlab/ci/config/entry/node.rb @@ -6,7 +6,7 @@ module Gitlab # Base abstract class for each configuration entry node. # class Node - class InvalidError < StandardError; end + InvalidError = Class.new(StandardError) attr_reader :config, :metadata attr_accessor :key, :parent, :description diff --git a/lib/gitlab/ci/config/loader.rb b/lib/gitlab/ci/config/loader.rb index dbf6eb0edbe..e7d9f6a7761 100644 --- a/lib/gitlab/ci/config/loader.rb +++ b/lib/gitlab/ci/config/loader.rb @@ -2,7 +2,7 @@ module Gitlab module Ci class Config class Loader - class FormatError < StandardError; end + FormatError = Class.new(StandardError) def initialize(config) @config = YAML.safe_load(config, [Symbol], [], true) diff --git a/lib/gitlab/conflict/file.rb b/lib/gitlab/conflict/file.rb index d80bc748209..75a213ef752 100644 --- a/lib/gitlab/conflict/file.rb +++ b/lib/gitlab/conflict/file.rb @@ -4,8 +4,7 @@ module Gitlab include Gitlab::Routing.url_helpers include IconsHelper - class MissingResolution < ResolutionError - end + MissingResolution = Class.new(ResolutionError) CONTEXT_LINES = 3 diff --git a/lib/gitlab/conflict/file_collection.rb b/lib/gitlab/conflict/file_collection.rb index fa5bd4649d4..990b719ecfd 100644 --- a/lib/gitlab/conflict/file_collection.rb +++ b/lib/gitlab/conflict/file_collection.rb @@ -1,8 +1,7 @@ module Gitlab module Conflict class FileCollection - class ConflictSideMissing < StandardError - end + ConflictSideMissing = Class.new(StandardError) attr_reader :merge_request, :our_commit, :their_commit diff --git a/lib/gitlab/conflict/parser.rb b/lib/gitlab/conflict/parser.rb index ddd657903fb..d3524c338ee 100644 --- a/lib/gitlab/conflict/parser.rb +++ b/lib/gitlab/conflict/parser.rb @@ -1,25 +1,15 @@ module Gitlab module Conflict class Parser - class UnresolvableError < StandardError - end - - class UnmergeableFile < UnresolvableError - end - - class UnsupportedEncoding < UnresolvableError - end + UnresolvableError = Class.new(StandardError) + UnmergeableFile = Class.new(UnresolvableError) + UnsupportedEncoding = Class.new(UnresolvableError) # Recoverable errors - the conflict can be resolved in an editor, but not with # sections. - class ParserError < StandardError - end - - class UnexpectedDelimiter < ParserError - end - - class MissingEndDelimiter < ParserError - end + ParserError = Class.new(StandardError) + UnexpectedDelimiter = Class.new(ParserError) + MissingEndDelimiter = Class.new(ParserError) def parse(text, our_path:, their_path:, parent_file: nil) raise UnmergeableFile if text.blank? # Typically a binary file diff --git a/lib/gitlab/conflict/resolution_error.rb b/lib/gitlab/conflict/resolution_error.rb index a0f2006bc24..0b61256b35a 100644 --- a/lib/gitlab/conflict/resolution_error.rb +++ b/lib/gitlab/conflict/resolution_error.rb @@ -1,6 +1,5 @@ module Gitlab module Conflict - class ResolutionError < StandardError - end + ResolutionError = Class.new(StandardError) end end diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb index b64db5d01ae..ec0529b5a4b 100644 --- a/lib/gitlab/email/receiver.rb +++ b/lib/gitlab/email/receiver.rb @@ -4,19 +4,19 @@ require_dependency 'gitlab/email/handler' # Inspired in great part by Discourse's Email::Receiver module Gitlab module Email - class ProcessingError < StandardError; end - class EmailUnparsableError < ProcessingError; end - class SentNotificationNotFoundError < ProcessingError; end - class ProjectNotFound < ProcessingError; end - class EmptyEmailError < ProcessingError; end - class AutoGeneratedEmailError < ProcessingError; end - class UserNotFoundError < ProcessingError; end - class UserBlockedError < ProcessingError; end - class UserNotAuthorizedError < ProcessingError; end - class NoteableNotFoundError < ProcessingError; end - class InvalidNoteError < ProcessingError; end - class InvalidIssueError < ProcessingError; end - class UnknownIncomingEmail < ProcessingError; end + ProcessingError = Class.new(StandardError) + EmailUnparsableError = Class.new(ProcessingError) + SentNotificationNotFoundError = Class.new(ProcessingError) + ProjectNotFound = Class.new(ProcessingError) + EmptyEmailError = Class.new(ProcessingError) + AutoGeneratedEmailError = Class.new(ProcessingError) + UserNotFoundError = Class.new(ProcessingError) + UserBlockedError = Class.new(ProcessingError) + UserNotAuthorizedError = Class.new(ProcessingError) + NoteableNotFoundError = Class.new(ProcessingError) + InvalidNoteError = Class.new(ProcessingError) + InvalidIssueError = Class.new(ProcessingError) + UnknownIncomingEmail = Class.new(ProcessingError) class Receiver def initialize(raw) diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb index d6b3b5705a9..2a017c93f57 100644 --- a/lib/gitlab/git/diff.rb +++ b/lib/gitlab/git/diff.rb @@ -2,7 +2,7 @@ module Gitlab module Git class Diff - class TimeoutError < StandardError; end + TimeoutError = Class.new(StandardError) include Gitlab::Git::EncodingHelper # Diff properties diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 8ec90885231..0e9b812ffdd 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -10,9 +10,9 @@ module Gitlab SEARCH_CONTEXT_LINES = 3 - class NoRepository < StandardError; end - class InvalidBlobName < StandardError; end - class InvalidRef < StandardError; end + NoRepository = Class.new(StandardError) + InvalidBlobName = Class.new(StandardError) + InvalidRef = Class.new(StandardError) # Full path to repo attr_reader :path diff --git a/lib/gitlab/import_export/error.rb b/lib/gitlab/import_export/error.rb index e341c4d9cf8..788eedf2686 100644 --- a/lib/gitlab/import_export/error.rb +++ b/lib/gitlab/import_export/error.rb @@ -1,5 +1,5 @@ module Gitlab module ImportExport - class Error < StandardError; end + Error = Class.new(StandardError) end end diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb index 95d2f559588..fcf51b7fc5b 100644 --- a/lib/gitlab/o_auth/user.rb +++ b/lib/gitlab/o_auth/user.rb @@ -5,7 +5,7 @@ # module Gitlab module OAuth - class SignupDisabledError < StandardError; end + SignupDisabledError = Class.new(StandardError) class User attr_accessor :auth_hash, :gl_user diff --git a/lib/gitlab/route_map.rb b/lib/gitlab/route_map.rb index 72d00abfcc2..36791fae60f 100644 --- a/lib/gitlab/route_map.rb +++ b/lib/gitlab/route_map.rb @@ -1,6 +1,6 @@ module Gitlab class RouteMap - class FormatError < StandardError; end + FormatError = Class.new(StandardError) def initialize(data) begin diff --git a/lib/gitlab/serializer/pagination.rb b/lib/gitlab/serializer/pagination.rb index bf2c0acc729..9c92b83dddc 100644 --- a/lib/gitlab/serializer/pagination.rb +++ b/lib/gitlab/serializer/pagination.rb @@ -1,7 +1,7 @@ module Gitlab module Serializer class Pagination - class InvalidResourceError < StandardError; end + InvalidResourceError = Class.new(StandardError) include ::API::Helpers::Pagination def initialize(request, response) diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb index 7374d2bc8b8..da8d8ddb8ed 100644 --- a/lib/gitlab/shell.rb +++ b/lib/gitlab/shell.rb @@ -2,7 +2,7 @@ require 'securerandom' module Gitlab class Shell - class Error < StandardError; end + Error = Class.new(StandardError) KeyAdder = Struct.new(:io) do def add_key(id, key) diff --git a/lib/gitlab/template/finders/repo_template_finder.rb b/lib/gitlab/template/finders/repo_template_finder.rb index 22c39436cb2..cb7957e2af9 100644 --- a/lib/gitlab/template/finders/repo_template_finder.rb +++ b/lib/gitlab/template/finders/repo_template_finder.rb @@ -4,7 +4,7 @@ module Gitlab module Finders class RepoTemplateFinder < BaseTemplateFinder # Raised when file is not found - class FileNotFoundError < StandardError; end + FileNotFoundError = Class.new(StandardError) def initialize(project, base_dir, extension, categories = {}) @categories = categories diff --git a/lib/gitlab/update_path_error.rb b/lib/gitlab/update_path_error.rb index ce14cc887d0..8947ecfb92e 100644 --- a/lib/gitlab/update_path_error.rb +++ b/lib/gitlab/update_path_error.rb @@ -1,3 +1,3 @@ module Gitlab - class UpdatePathError < StandardError; end + UpdatePathError = Class.new(StandardError) end diff --git a/lib/mattermost/client.rb b/lib/mattermost/client.rb index e55c0d6ac49..ad6df246091 100644 --- a/lib/mattermost/client.rb +++ b/lib/mattermost/client.rb @@ -1,5 +1,5 @@ module Mattermost - class ClientError < Mattermost::Error; end + ClientError = Class.new(Mattermost::Error) class Client attr_reader :user diff --git a/lib/mattermost/error.rb b/lib/mattermost/error.rb index 014df175be0..dee6deb7974 100644 --- a/lib/mattermost/error.rb +++ b/lib/mattermost/error.rb @@ -1,3 +1,3 @@ module Mattermost - class Error < StandardError; end + Error = Class.new(StandardError) end diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb index 377cb7b1021..5388966605d 100644 --- a/lib/mattermost/session.rb +++ b/lib/mattermost/session.rb @@ -5,7 +5,7 @@ module Mattermost end end - class ConnectionError < Mattermost::Error; end + ConnectionError = Class.new(Mattermost::Error) # This class' prime objective is to obtain a session token on a Mattermost # instance with SSO configured where this GitLab instance is the provider. diff --git a/rubocop/cop/custom_error_class.rb b/rubocop/cop/custom_error_class.rb new file mode 100644 index 00000000000..38d93acfe88 --- /dev/null +++ b/rubocop/cop/custom_error_class.rb @@ -0,0 +1,64 @@ +module RuboCop + module Cop + # This cop makes sure that custom error classes, when empty, are declared + # with Class.new. + # + # @example + # # bad + # class FooError < StandardError + # end + # + # # okish + # class FooError < StandardError; end + # + # # good + # FooError = Class.new(StandardError) + class CustomErrorClass < RuboCop::Cop::Cop + MSG = 'Use `Class.new(SuperClass)` to define an empty custom error class.'.freeze + + def on_class(node) + _klass, parent, body = node.children + + return if body + + parent_klass = class_name_from_node(parent) + + return unless parent_klass && parent_klass.to_s.end_with?('Error') + + add_offense(node, :expression) + end + + def autocorrect(node) + klass, parent, _body = node.children + replacement = "#{class_name_from_node(klass)} = Class.new(#{class_name_from_node(parent)})" + + lambda do |corrector| + corrector.replace(node.source_range, replacement) + end + end + + private + + # The nested constant `Foo::Bar::Baz` looks like: + # + # s(:const, + # s(:const, + # s(:const, nil, :Foo), :Bar), :Baz) + # + # So recurse through that to get the name as written in the source. + # + def class_name_from_node(node, suffix = nil) + return unless node&.type == :const + + name = node.children[1].to_s + name = "#{name}::#{suffix}" if suffix + + if node.children[0] + class_name_from_node(node.children[0], name) + else + name + end + end + end + end +end diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb index d4266d0deae..ea8e0f64b0d 100644 --- a/rubocop/rubocop.rb +++ b/rubocop/rubocop.rb @@ -1,3 +1,4 @@ +require_relative 'cop/custom_error_class' require_relative 'cop/gem_fetcher' require_relative 'cop/migration/add_column' require_relative 'cop/migration/add_column_with_default' diff --git a/spec/rubocop/cop/custom_error_class_spec.rb b/spec/rubocop/cop/custom_error_class_spec.rb new file mode 100644 index 00000000000..381d7871a40 --- /dev/null +++ b/spec/rubocop/cop/custom_error_class_spec.rb @@ -0,0 +1,111 @@ +require 'spec_helper' + +require 'rubocop' +require 'rubocop/rspec/support' + +require_relative '../../../rubocop/cop/custom_error_class' + +describe RuboCop::Cop::CustomErrorClass do + include CopHelper + + subject(:cop) { described_class.new } + + context 'when a class has a body' do + it 'does nothing' do + inspect_source(cop, 'class CustomError < StandardError; def foo; end; end') + + expect(cop.offenses).to be_empty + end + end + + context 'when a class has no explicit superclass' do + it 'does nothing' do + inspect_source(cop, 'class CustomError; end') + + expect(cop.offenses).to be_empty + end + end + + context 'when a class has a superclass that does not end in Error' do + it 'does nothing' do + inspect_source(cop, 'class CustomError < BasicObject; end') + + expect(cop.offenses).to be_empty + end + end + + context 'when a class is empty and inherits from a class ending in Error' do + context 'when the class is on a single line' do + let(:source) do + <<-SOURCE + module Foo + class CustomError < Bar::Baz::BaseError; end + end + SOURCE + end + + let(:expected) do + <<-EXPECTED + module Foo + CustomError = Class.new(Bar::Baz::BaseError) + end + EXPECTED + end + + it 'registers an offense' do + expected_highlights = source.split("\n")[1].strip + + inspect_source(cop, source) + + aggregate_failures do + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.map(&:line)).to eq([2]) + expect(cop.highlights).to contain_exactly(expected_highlights) + end + end + + it 'autocorrects to the right version' do + autocorrected = autocorrect_source(cop, source, 'foo/custom_error.rb') + + expect(autocorrected).to eq(expected) + end + end + + context 'when the class is on multiple lines' do + let(:source) do + <<-SOURCE + module Foo + class CustomError < Bar::Baz::BaseError + end + end + SOURCE + end + + let(:expected) do + <<-EXPECTED + module Foo + CustomError = Class.new(Bar::Baz::BaseError) + end + EXPECTED + end + + it 'registers an offense' do + expected_highlights = source.split("\n")[1..2].join("\n").strip + + inspect_source(cop, source) + + aggregate_failures do + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.map(&:line)).to eq([2]) + expect(cop.highlights).to contain_exactly(expected_highlights) + end + end + + it 'autocorrects to the right version' do + autocorrected = autocorrect_source(cop, source, 'foo/custom_error.rb') + + expect(autocorrected).to eq(expected) + end + end + end +end