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

Add cop to prevent accidental writing to stdout #2917

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require:
- rubocop-md
- rubocop-minitest
- rubocop-rake
- ./lib/rubocop/cop/ruby_lsp/output
- ./lib/rubocop/cop/ruby_lsp/use_language_server_aliases
- ./lib/rubocop/cop/ruby_lsp/use_register_with_handler_method

Expand Down Expand Up @@ -33,6 +34,10 @@ RubyLsp/UseLanguageServerAliases:
Exclude:
- "test/**/*.rb"

RubyLsp/Output:
Include:
- "**/*.rb"

Sorbet/FalseSigil:
Enabled: false

Expand Down Expand Up @@ -60,6 +65,7 @@ Sorbet/StrictSigil:
- "lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb"
- "lib/ruby_lsp/load_sorbet.rb"
- "lib/ruby_lsp/scripts/compose_bundle.rb"
- "lib/rubocop/cop/ruby_lsp/output.rb"

Layout/ClassStructure:
Enabled: true
Expand Down
68 changes: 68 additions & 0 deletions lib/rubocop/cop/ruby_lsp/output.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# typed: false
# frozen_string_literal: true

require "rubocop"
require "sorbet-runtime"

module RuboCop
module Cop
module RubyLsp
class Output < Base
include RangeHelp

MSG = "Do not write to stdout as it can break the LSP communication. " \
"Remove it, or use `Notification.window_log_message` instead."
RESTRICT_ON_SEND = [
:ap,
:p,
:pp,
:pretty_print,
:print,
:puts,
:binwrite,
:syswrite,
:write,
:write_nonblock,
].freeze
ALLOWED_TYPES = [:send, :csend, :block, :numblock].freeze

def_node_matcher :output?, <<~PATTERN
(send nil? {:ap :p :pp :pretty_print :print :puts} ...)
PATTERN

def_node_matcher :io_output?, <<~PATTERN
(send
{
(gvar #match_gvar?)
(const {nil? cbase} {:STDOUT :STDERR})
}
{:binwrite :syswrite :write :write_nonblock}
...)
PATTERN

def on_send(node)
return if ALLOWED_TYPES.include?(node.parent&.type)
return if !output?(node) && !io_output?(node)

range = offense_range(node)

add_offense(range)
end

private

def match_gvar?(sym)
[:$stdout, :$stderr].include?(sym)
end

def offense_range(node)
if node.receiver
range_between(node.source_range.begin_pos, node.loc.selector.end_pos)
else
node.loc.selector
end
end
end
end
end
end
13 changes: 10 additions & 3 deletions project-words
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ bigdecimal
bindir
binmode
binread
binstub
binwrite
Bizt
Bizw
bufnr
binstub
bytesize
byteslice
cbase
codepoint
codepoints
concats
copen
Corge
csend
dont
eglot
Eglot
Expand All @@ -28,6 +31,7 @@ Floo
fnmatch
fooo
gemname
gvar
hostedtoolcache
importmap
indexables
Expand All @@ -50,14 +54,16 @@ mkpath
multibyte
nargs
nodoc
nonblock
noreturn
numblock
nvim
popen
qorge
qtlzwssomeking
quickfixes
quxx
quux
qorge
quxx
rdbg
readlines
realpath
Expand All @@ -83,6 +89,7 @@ strscan
subexpression
supertypes
suppo
syswrite
unaliased
unindexed
unparser
Expand Down
Loading