Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Static typo checker

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Static typo checker

Avatar for Yuki Nishijima

Yuki Nishijima

September 19, 2017
Tweet

More Decks by Yuki Nishijima

Other Decks in Programming

Transcript

  1. 4 U B U J D  U Z Q

    P  D I F D L F S
  2. 8 I B U  E P F T 

    U I F  E J E @ Z P V @ N F B O  H F N  E P
  3. 3 F D F O U  D I B

    O H F T  J O  U I F  d i d _ y o u _ m e a n  H F N
  4. %: .        ❤

     3 V C Z       w %:.BMQIBDPNQBUJCMFXJUIEFW w %:.DPNQBUJCMFXJUIBOEMBUFS w %:.DPNQBUJCMFXJUIBOEMBUFS w %:.GPSBOZFBSMJFSWFSTJPOTPG3VCZ
  5. 4 V H H F T U J P O

    T  P O  / B N F & S S P S  G S P N  S t r u c t # [ ] Struct.new(:name).new[:naem] # => NameError: no member 'naem' in struct # Did you mean? name Struct.new(:name).new[:naem] # => NameError: no member 'naem' in struct Ruby 2.4 and did_you_mean 1.1.2: Ruby 2.3 and did_you_mean 1.0.0:
  6. $ B M M F E  O B N

    F  J T  F Y D M V E F E  G S P N  T V H H F T U J P O T name; name = 1 # => NameError: undefined local variable or method `name' for … # Did you mean? name name; name = 1 # => NameError: undefined local variable or method `name' for … Ruby 2.4 and did_you_mean 1.1.2: Ruby 2.3 and did_you_mean 1.0.0:
  7. - F T T  T V H H F

    T U J P O T  P O  / P . F U I P E & S S P S  G S P N  n i l @users.map {|user| ... } # => NameError: undefined method `map' for nil:NilClass # Did you mean? tap @users.map {|user| ... } # => NameError: undefined method `map' for nil:NilClass Ruby 2.4 and did_you_mean 1.1.2: Ruby 2.3 and did_you_mean 1.0.0:
  8. 1 S J W B U F  N F

    U I P E T  B S F  O P  M P O H F S  T V H H F T U F E  X I F O  O P U  D B M M B C M F File.raed 'path/to/file.csv' # => NoMethodError: undefined method `raed' for File:Class # Did you mean? read # rand File.raed 'path/to/file.csv' # => NoMethodError: undefined method `raed' for File:Class # Did you mean? read Ruby 2.4 and did_you_mean 1.1.2: Ruby 2.3 and did_you_mean 1.0.0:
  9. & Y Q F S J N F O U

    B M  G F B U V S F T
  10. require 'did_you_mean/experimental' @full_name = "Yuki Nishijima" @full_anme.split(" ") # =>

    NoMethodError: undefined method `split' for nil:NilClass # Did you mean? @full_name
  11. class User def iniialize(name, ...) ... end end User.new("Yuki Nishijima",

    ...) # => ArgumentError: wrong number of arguments … 5ZQP
  12. require 'did_you_mean/experimental' class User def iniialize(name, ...) ... end end

    # => warning: iniialize might be misspelled, perhaps you meant initialize? User.new("Yuki Nishijima", ...) # => ArgumentError: wrong number of arguments …
  13. 4 Q F M M  D I F D

    L F S  B T  B  Q V C M J D  " 1 * checker = DidYouMean::SpellChecker.new(dictionary: methods) checker.correct(‘sedn') # => [‘send']
  14. + 3 V C Z  T V Q Q

    P S U $ irb > RUBY_ENGINE # => "jruby" > RUBY_DESCRIPTION # => "jruby 9.1.3.0 (2.3.1) 2016-08-29 a2a3b29 Java HotSpot(TM) 64-Bit Server VM 25.121-b13 on 1.8.0_121-b13 +jit [darwin-x86_64]” > "Yuki".starts_with?("Y") # => NoMethodError: undefined method `starts_with?' for … Did you mean? start_with?
  15. 1 S P C M F N T  J

    O  U I F  d i d _ y o u _ m e a n  H F N
  16. - P B E J O H  :P V

    S  " Q Q M J D B U J P O w 3VCZDPSF .3* +3VCZ 3VCJOJVT FUD  w 3VCZHFNT OPUHFNT  w :PVSDPEF w %FQFOEFODJFT TUEMJC HFNT  w :PVSBQQ`TDMBTTFTNPEVMFT w .FUBQSPHSBNNJOH #define_method #eval  w 0UIFSXPSLJGOFDFTTBSZ FHMJTUFOJOHUPBQPSU
  17. 3 V O O J O H  :P V

    S  " Q Q M J D B U J P O w 8FCTFSWFSSFDFJWFTBSFRVFTU w 3FRVFTUJTQBTTFEJOUPrack w 3FRVFTUHPFTUISPVHISBDLNJEEMFXBSF w 3FRVFTUIJUTUIF3BJMTDPOUSPMMFS w $POUSPMMFSHFOFSBUFTBSFTQPOTF w %# )551DBMMT DBDIJOH MPHHJOH )5.- FUD w 4FOEFWFSZUIJOHCBDLUPUIFDMJFOU
  18. w 8FCTFSWFSSFDFJWFTBSFRVFTU w 3FRVFTUJTQBTTFEJOUPrack w 3FRVFTUHPFTUISPVHIrack middleware w 3FRVFTUIJUTUIFRailsDPOUSPMMFS w

    $POUSPMMFSHFOFSBUFTBSFTQPOTF w %# )551DBMMT DBDIJOH MPHHJOH )5.- FUD w 4FOEFWFSZUIJOHCBDLUPUIFDMJFOU w 3VCZDPSF .3* +3VCZ 3VCJOJVT FUD  w 3VCZHFNT OPUHFNT  w :PVSDPEF w %FQFOEFODJFT TUEMJC HFNT  w :PVSDMBTTFTNPEVMFT w .FUBQSPHSBNNJOH #define_method #eval  w 0UIFSXPSLJGOFDFTTBSZ FHMJTUFOJOHUPBQPSU
  19. w 3VCZDPSF .3* +3VCZ 3VCJOJVT FUD  w 3VCZHFNT OPUHFNT

     w :PVSDPEF w %FQFOEFODJFT TUEMJC HFNT  w :PVSDMBTTFTNPEVMFT w .FUBQSPHSBNNJOH #define_method #eval  w 0UIFSXPSLJGOFDFTTBSZ FHMJTUFOJOHUPBQPSU w 8FCTFSWFSSFDFJWFTBSFRVFTU w 3FRVFTUJTQBTTFEJOUPrack w 3FRVFTUHPFTUISPVHIrack middleware w 3FRVFTUIJUTUIFRailsDPOUSPMMFS w $POUSPMMFSHFOFSBUFTBSFTQPOTF w %# )551DBMMT DBDIJOH MPHHJOH )5.- FUD w NoMethodErrorPDDVST w did_you_meanTVHHFTUTBNFUIPEOBNF
  20. 4 U B U J D  B O B

    M Z T J T  U P  U I F  S F T D V F
  21. d i d _ y o u _ m e

    a n / s t a t i c require 'did_you_mean/undefined_method_detector' PROJECT_DIR = Dir.pwd at_exit { UndefinedMethodDetector.new(PROJECT_DIR) .undefined_methods .each { |method| method.called_by.each { |caller| puts "`#{method.name}' seems undefined but is called at " \ "#{caller.source_location[0]}:#{method.lineno}" } } }
  22. d i d _ y o u _ m e

    a n / s t a t i c require 'did_you_mean/undefined_method_detector' PROJECT_DIR = Dir.pwd at_exit { UndefinedMethodDetector.new(PROJECT_DIR) .undefined_methods .each { |method| method.called_by.each { |caller| puts "`#{method.name}' seems undefined but is called at " \ "#{caller.source_location[0]}:#{method.lineno}" } } }
  23. 1B S T F S w Ripper w UIFQBSTFSHFN w

    RubyVM::InstructionSequence CZUFDPEF 
  24. require ‘pp' pp RubyVM::InstructionSequence.compile(<<-CODE).to_a[13] class Foo def bar i_do_not_exist end

    end CODE # => bytecode [1, [:trace, 1], [:putspecialobject, 3], [:putnil], [:defineclass, :Foo, ["YARVInstructionSequence/SimpleDataFormat", 2, 3, 1, {:arg_size=>0, :local_size=>0, :stack_max=>3}, "<class:Foo>", "<compiled>", nil, 1, :class, [], {}, [], [1, [:trace, 2], 2, [:trace, 1], [:putspecialobject, 1], [:putobject, :bar], [:putiseq, ["YARVInstructionSequence/SimpleDataFormat", 2, 3, 1, {:arg_size=>0, :local_size=>0, :stack_max=>1}, "bar", "<compiled>", nil, 2, :method, [], {}, [], [2, [:trace, 8], 3, [:trace, 1], [:putself], [:opt_send_without_block, {:mid=>:i_do_not_exist, :flag=>28, :orig_argc=>0}, false], 4, [:trace, 16], 3, [:leave]]]], [:opt_send_without_block, {:mid=>:"core#define_method", :flag=>16, :orig_argc=>2}, false], 5, [:trace, 4], 2, [:leave]]], 0], [:leave]]
  25. class Foo def bar i_do_not_exist end end [:defineclass, :Foo, […,

    […, [:putobject, :bar], […, …, …, [:opt_send_without_block, {:mid=>:i_do_not_exist, :flag=>28, :orig_argc=>0}, false], …,]]]]
  26. class Foo def bar i_do_not_exist end end [:defineclass, :Foo, […,

    […, [:putobject, :bar], […, …, …, [:opt_send_without_block, {:mid=>:i_do_not_exist, :flag=>28, :orig_argc=>0}, false], …,]]]]
  27. $ B M M B C M F  N

    F U I P E T • Foo.instance_methods • Foo.private_instance_methods
  28. $ B M M B C M F  N

    F U I P E T • Foo.instance_methods • Foo.private_instance_methods l*TO`UUIBU3VCZDPEF z"
  29. 1 S P C M F N T w /PUFOUJSFMZTUBUJD

    w 0OMZTLJQTFYFDVUJOHJOTUBODFNFUIPET w $MBTTFTBOENPEVMFTOFFEUPCFMPBEFE w ,FSOFMNFUIPETDBOCFDBMMBCMF #eval #exit FUD 
  30. & E J U P S  J O U

    F H S B U J P O
  31. " D U J W B U F  7

    4 $ P E F  Q M V H J O export function activate(context: ExtensionContext) { … const window = vscode.window const subscription = window.onDidChangeActiveTextEditor(executeSpellChecking) context.subscriptions.push(subscription) }
  32. " D U J W B U F  7

    4 $ P E F  Q M V H J O export function activate(context: ExtensionContext) { … const window = vscode.window const subscription = window.onDidChangeActiveTextEditor(executeSpellChecking) context.subscriptions.push(subscription) }
  33. function executeSpellChecking( editor: vscode.TextEditor | vscode.TextDocumentChangeEvent) { if (!editor ||

    editor.document.isDirty) return const projectDir = vscode.workspace.rootPath const fileToCheck = editor.document.uri.path … cp.exec( `ruby -rdid_you_mean/static -Ilib ${fileToCheck}`, { cwd: projectDir }, (err, stdout, stderr) => { const result = JSON.parse(stdout) // => result from DYM // Annotate undefined methods using VSCode API }) }
  34. function executeSpellChecking( editor: vscode.TextEditor | vscode.TextDocumentChangeEvent) { if (!editor ||

    editor.document.isDirty) return const projectDir = vscode.workspace.rootPath const fileToCheck = editor.document.uri.path … cp.exec( `ruby -rdid_you_mean/static -Ilib ${fileToCheck}`, { cwd: projectDir }, (err, stdout, stderr) => { const result = JSON.parse(stdout) // => result from DYM // Annotate undefined methods using VSCode API }) }
  35. function executeSpellChecking( editor: vscode.TextEditor | vscode.TextDocumentChangeEvent) { if (!editor ||

    editor.document.isDirty) return const projectDir = vscode.workspace.rootPath const fileToCheck = editor.document.uri.path … cp.exec( `ruby -rdid_you_mean/static -Ilib ${fileToCheck}`, { cwd: projectDir }, (err, stdout, stderr) => { const result = JSON.parse(stdout) // => result from DYM // Annotate undefined methods using VSCode API }) }
  36. { "ruby_engine": "ruby", "ruby_version": "2.5.0", "ruby_description": "ruby 2.5.0dev (2017-06-28 trunk

    59186) …”, "did_you_mean_version": "1.2.0-alpha", "undefined_names": { "9": [ { "undefined_name": "raiae", "symbol_type": "method", "path": "/did_you_mean/test/spell_checking/method_name_test.rb", "lineno": 9, "suggestions": [ "raise" ] } ], … }
  37. require 'did_you_mean/undefined_method_detector' PROJECT_DIR = Dir.pwd at_exit { UndefinedMethodDetector.new(PROJECT_DIR) .undefined_methods .each

    { |method| method.called_by.each { |caller| puts "`#{method.name}' seems undefined but is called at " \ "#{caller.source_location[0]}:#{method.lineno}" } } }
  38. require 'did_you_mean/undefined_method_detector' PROJECT_DIR = Dir.pwd at_exit { UndefinedMethodDetector.new(PROJECT_DIR) .undefined_methods .each

    { |method| method.called_by.each { |caller| puts "`#{method.name}' seems undefined but is called at " \ "#{caller.source_location[0]}:#{method.lineno}" } } } the #puts call => JSON.dump
  39. $ V S S F O U  T U

    B U F w /PUFOUJSFMZTUBUJD w 5PPNBOZGBMTFQPTJUJWFT w /PUGBTUFOPVHIGPSMBSHFQSPKFDUT w 3BJMT
  40. 4 X J U D I J O H 

    U P  T U B U J D  B O B M Z T J T w 5FDIOJDBMMZQPTTJCMF w NBZNBLFJUJOBDDVSBUF
  41. 8 I Z  N B O Z  G

    B M T F  Q P T J U J W F T w %ZOBNJDBMMZEFpOFENFUIPET w $BMMTIBOEMFECZ#method_missing w %VDLUZQJOH
  42. 8 I Z  T M P X w 5PPNBOZEFQFOEFODJFTUPMPBE

    w 'JOEJOHTVHHFTUJPOTJTTMPX
  43. { "ruby_engine": "ruby", "ruby_version": "2.5.0", "ruby_description": "ruby 2.5.0dev (2017-06-28 trunk

    59186) …”, "did_you_mean_version": "1.2.0-alpha", "undefined_names": { "9": [ { "undefined_name": "raiae", "symbol_type": "method", "path": "/did_you_mean/test/spell_checking/method_name_test.rb", "lineno": 9, "suggestions": [ "raise" ] } ], … } Suggestion lookup is time-consuming, this may be skipped depending on the project size
  44. $ P O D M V T J P O

    w 4UJMMJOFBSMZTUBHF CVUXPSLTXFMMGPSTNBMMQSPKFDUT w 5ZQFTZTUFNXJMMIFMQ CVUTFFNTUPPBNCJUJPVTBUUIJT QPJOU w 4UBUJDBOBMZTJT PS"45 JTFYUSFNFMZQPXFSGVM