<commit_before>/// This list represents the default file types that ripgrep ships with. In
/// general, any file format is fair game, although it should generally be
/// limited to reasonably popular open formats. For other cases, you can add
/// types to each invocation of ripgrep with the '--type-add' flag.
///
/// If you would like to add or improve this list, please file a PR:
/// <https://github.com/BurntSushi/ripgrep>.
///
/// Please try to keep this list sorted lexicographically and wrapped to 79
/// columns (inclusive).
#[rustfmt::skip]
pub const DEFAULT_TYPES: &[(&str, &[&str])] = &[
    ("agda", &["*.agda", "*.lagda"]),
    ("aidl", &["*.aidl"]),
    ("amake", &["*.mk", "*.bp"]),
    ("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("asp", &[
        "*.aspx", "*.aspx.cs", "*.aspx.vb", "*.ascx", "*.ascx.cs",
        "*.ascx.vb", "*.asp"
    ]),
    ("ats", &["*.ats", "*.dats", "*.sats", "*.hats"]),
    ("avro", &["*.avdl", "*.avpr", "*.avsc"]),
    ("awk", &["*.awk"]),
    ("bazel", &[
        "*.bazel", "*.bzl", "*.BUILD", "*.bazelrc", "BUILD", "MODULE.bazel",
        "WORKSPACE", "WORKSPACE.bazel",
    ]),
    ("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
    ("brotli", &["*.br"]),
    ("buildstream", &["*.bst"]),
    ("bzip2", &["*.bz2", "*.tbz2"]),
    ("c", &["*.[chH]", "*.[chH].in", "*.cats"]),
    ("cabal", &["*.cabal"]),
    ("cbor", &["*.cbor"]),
    ("ceylon", &["*.ceylon"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("config", &["*.cfg", "*.conf", "*.config", "*.ini"]),
    ("coq", &["*.v"]),
    ("cpp", &[
        "*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh",  "*.inl",
        "*.[ChH].in", "*.cc.in", "*.[ch]pp.in", "*.[ch]xx.in", "*.hh.in",
    ]),
    ("creole", &["*.creole"]),
    ("crystal", &["Projectfile", "*.cr", "*.ecr", "shard.yml"]),
    ("cs", &["*.cs"]),
    ("csharp", &["*.cs"]),
    ("cshtml", &["*.cshtml"]),
    ("css", &["*.css", "*.scss"]),
    ("csv", &["*.csv"]),
    ("cuda", &["*.cu", "*.cuh"]),
    ("cython", &["*.pyx", "*.pxi", "*.pxd"]),
    ("d", &["*.d"]),
    ("dart", &["*.dart"]),
    ("dhall", &["*.dhall"]),
    ("diff", &["*.patch", "*.diff"]),
    ("docker", &["*Dockerfile*"]),
    ("dts", &["*.dts", "*.dtsi"]),
    ("dvc", &["Dvcfile", "*.dvc"]),
    ("ebuild", &["*.ebuild"]),
    ("edn", &["*.edn"]),
    ("elisp", &["*.el"]),
    ("elixir", &["*.ex", "*.eex", "*.exs"]),
    ("elm", &["*.elm"]),
    ("erb", &["*.erb"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fennel", &["*.fnl"]),
    ("fidl", &["*.fidl"]),
    ("fish", &["*.fish"]),
    ("flatbuffers", &["*.fbs"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("fut", &["*.fut"]),
    ("gap", &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
    ("gn", &["*.gn", "*.gni"]),
    ("go", &["*.go"]),
    ("gradle", &["*.gradle"]),
    ("groovy", &["*.groovy", "*.gradle"]),
    ("gzip", &["*.gz", "*.tgz"]),
    ("h", &["*.h", "*.hh", "*.hpp"]),
    ("haml", &["*.haml"]),
    ("hare", &["*.ha"]),
    ("haskell", &["*.hs", "*.lhs", "*.cpphs", "*.c2hs", "*.hsc"]),
    ("hbs", &["*.hbs"]),
    ("hs", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html", "*.ejs"]),
    ("hy", &["*.hy"]),
    ("idris", &["*.idr", "*.lidr"]),
    ("janet", &["*.janet"]),
    ("java", &["*.java", "*.jsp", "*.jspx", "*.properties"]),
    ("jinja", &["*.j2", "*.jinja", "*.jinja2"]),
    ("jl", &["*.jl"]),
    ("js", &["*.js", "*.jsx", "*.vue"]),
    ("json", &["*.json", "composer.lock"]),
    ("jsonl", &["*.jsonl"]),
    ("julia", &["*.jl"]),
    ("jupyter", &["*.ipynb", "*.jpynb"]),
    ("k", &["*.k"]),
    ("kotlin", &["*.kt", "*.kts"]),
    ("less", &["*.less"]),
    ("license", &[
        // General
        "COPYING", "COPYING[.-]*",
        "COPYRIGHT", "COPYRIGHT[.-]*",
        "EULA", "EULA[.-]*",
        "licen[cs]e", "licen[cs]e.*",
        "LICEN[CS]E", "LICEN[CS]E[.-]*", "*[.-]LICEN[CS]E*",
        "NOTICE", "NOTICE[.-]*",
        "PATENTS", "PATENTS[.-]*",
        "UNLICEN[CS]E", "UNLICEN[CS]E[.-]*",
        // GPL (gpl.txt, etc.)
        "agpl[.-]*",
        "gpl[.-]*",
        "lgpl[.-]*",
        // Other license-specific (APACHE-2.0.txt, etc.)
        "AGPL-*[0-9]*",
        "APACHE-*[0-9]*",
        "BSD-*[0-9]*",
        "CC-BY-*",
        "GFDL-*[0-9]*",
        "GNU-*[0-9]*",
        "GPL-*[0-9]*",
        "LGPL-*[0-9]*",
        "MIT-*[0-9]*",
        "MPL-*[0-9]*",
        "OFL-*[0-9]*",
    ]),
    ("lilypond", &["*.ly", "*.ily"]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lock", &["*.lock", "package-lock.json"]),
    ("log", &["*.log"]),
    ("lua", &["*.lua"]),
    ("lz4", &["*.lz4"]),
    ("lzma", &["*.lzma"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &[
        "[Gg][Nn][Uu]makefile", "[Mm]akefile",
        "[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
        "[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
        "*.mk", "*.mak"
    ]),
    ("mako", &["*.mako", "*.mao"]),
    ("man", &["*.[0-9lnpx]", "*.[0-9][cEFMmpSx]"]),
    ("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkd", "*.mkdn"]),
    ("matlab", &["*.m"]),
    ("md", &["*.markdown", "*.md", "*.mdown", "*.mkd", "*.mkdn"]),
    ("meson", &["meson.build", "meson_options.txt"]),
    ("minified", &["*.min.html", "*.min.css", "*.min.js"]),
    ("mint", &["*.mint"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("msbuild", &[
        "*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
    ]),
    ("nim", &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
    ("nix", &["*.nix"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("org", &["*.org", "*.org_archive"]),
    ("pants", &["BUILD"]),
    ("pascal", &["*.pas", "*.dpr", "*.lpr", "*.pp", "*.inc"]),
    ("pdf", &["*.pdf"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm", "*.t"]),
    ("php", &[
        // note that PHP 6 doesn't exist
        // See: https://wiki.php.net/rfc/php6
        "*.php", "*.php3", "*.php4", "*.php5", "*.php7", "*.php8",
        "*.pht", "*.phtml"
    ]),
    ("po", &["*.po"]),
    ("pod", &["*.pod"]),
    ("postscript", &["*.eps", "*.ps"]),
    ("protobuf", &["*.proto"]),
    ("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
    ("puppet", &["*.erb", "*.pp", "*.rb"]),
    ("purs", &["*.purs"]),
    ("py", &["*.py"]),
    ("qmake", &["*.pro", "*.pri", "*.prf"]),
    ("qml", &["*.qml"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("racket", &["*.rkt"]),
    ("rdoc", &["*.rdoc"]),
    ("readme", &["README*", "*README"]),
    ("red", &["*.r", "*.red", "*.reds"]),
    ("robot", &["*.robot"]),
    ("rst", &["*.rst"]),
    ("ruby", &[
        // Idiomatic files
        "config.ru", "Gemfile", ".irbrc", "Rakefile",
        // Extensions
        "*.gemspec", "*.rb", "*.rbw"
    ]),
    ("rust", &["*.rs"]),
    ("sass", &["*.sass", "*.scss"]),
    ("scala", &["*.scala", "*.sbt"]),
    ("sh", &[
        // Portable/misc. init files
        ".login", ".logout", ".profile", "profile",
        // bash-specific init files
        ".bash_login", "bash_login",
        ".bash_logout", "bash_logout",
        ".bash_profile", "bash_profile",
        ".bashrc", "bashrc", "*.bashrc",
        // csh-specific init files
        ".cshrc", "*.cshrc",
        // ksh-specific init files
        ".kshrc", "*.kshrc",
        // tcsh-specific init files
        ".tcshrc",
        // zsh-specific init files
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        // Extensions
        "*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
    ]),
    ("slim", &["*.skim", "*.slim", "*.slime"]),
    ("smarty", &["*.tpl"]),
    ("sml", &["*.sml", "*.sig"]),
    ("soy", &["*.soy"]),
    ("spark", &["*.spark"]),
    ("spec", &["*.spec"]),
    ("sql", &["*.sql", "*.psql"]),
    ("stylus", &["*.styl"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("svg", &["*.svg"]),
    ("swift", &["*.swift"]),
    ("swig", &["*.def", "*.i"]),
    ("systemd", &[
        "*.automount", "*.conf", "*.device", "*.link", "*.mount", "*.path",
        "*.scope", "*.service", "*.slice", "*.socket", "*.swap", "*.target",
        "*.timer",
    ]),
    ("taskpaper", &["*.taskpaper"]),
    ("tcl", &["*.tcl"]),
    ("tex", &["*.tex", "*.ltx", "*.cls", "*.sty", "*.bib", "*.dtx", "*.ins"]),
    ("texinfo", &["*.texi"]),
    ("textile", &["*.textile"]),
    ("tf", &["*.tf"]),
    ("thrift", &["*.thrift"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("twig", &["*.twig"]),
    ("txt", &["*.txt"]),
    ("typoscript", &["*.typoscript", "*.ts"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vcl", &["*.vcl"]),
    ("verilog", &["*.v", "*.vh", "*.sv", "*.svh"]),
    ("vhdl", &["*.vhd", "*.vhdl"]),
    ("vim", &[
        "*.vim", ".vimrc", ".gvimrc", "vimrc", "gvimrc", "_vimrc", "_gvimrc",
    ]),
    ("vimscript", &[
        "*.vim", ".vimrc", ".gvimrc", "vimrc", "gvimrc", "_vimrc", "_gvimrc",
    ]),
    ("webidl", &["*.idl", "*.webidl", "*.widl"]),
    ("wiki", &["*.mediawiki", "*.wiki"]),
    ("xml", &[
        "*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
        "*.rng", "*.sch", "*.xhtml",
    ]),
    ("xz", &["*.xz", "*.txz"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
    ("yang", &["*.yang"]),
    ("z", &["*.Z"]),
    ("zig", &["*.zig"]),
    ("zsh", &[
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        "*.zsh",
    ]),
    ("zstd", &["*.zst", "*.zstd"]),
];
<commit_msg>ignore/types: add Solidity<commit_after>/// This list represents the default file types that ripgrep ships with. In
/// general, any file format is fair game, although it should generally be
/// limited to reasonably popular open formats. For other cases, you can add
/// types to each invocation of ripgrep with the '--type-add' flag.
///
/// If you would like to add or improve this list, please file a PR:
/// <https://github.com/BurntSushi/ripgrep>.
///
/// Please try to keep this list sorted lexicographically and wrapped to 79
/// columns (inclusive).
#[rustfmt::skip]
pub const DEFAULT_TYPES: &[(&str, &[&str])] = &[
    ("agda", &["*.agda", "*.lagda"]),
    ("aidl", &["*.aidl"]),
    ("amake", &["*.mk", "*.bp"]),
    ("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("asp", &[
        "*.aspx", "*.aspx.cs", "*.aspx.vb", "*.ascx", "*.ascx.cs",
        "*.ascx.vb", "*.asp"
    ]),
    ("ats", &["*.ats", "*.dats", "*.sats", "*.hats"]),
    ("avro", &["*.avdl", "*.avpr", "*.avsc"]),
    ("awk", &["*.awk"]),
    ("bazel", &[
        "*.bazel", "*.bzl", "*.BUILD", "*.bazelrc", "BUILD", "MODULE.bazel",
        "WORKSPACE", "WORKSPACE.bazel",
    ]),
    ("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
    ("brotli", &["*.br"]),
    ("buildstream", &["*.bst"]),
    ("bzip2", &["*.bz2", "*.tbz2"]),
    ("c", &["*.[chH]", "*.[chH].in", "*.cats"]),
    ("cabal", &["*.cabal"]),
    ("cbor", &["*.cbor"]),
    ("ceylon", &["*.ceylon"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("config", &["*.cfg", "*.conf", "*.config", "*.ini"]),
    ("coq", &["*.v"]),
    ("cpp", &[
        "*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh",  "*.inl",
        "*.[ChH].in", "*.cc.in", "*.[ch]pp.in", "*.[ch]xx.in", "*.hh.in",
    ]),
    ("creole", &["*.creole"]),
    ("crystal", &["Projectfile", "*.cr", "*.ecr", "shard.yml"]),
    ("cs", &["*.cs"]),
    ("csharp", &["*.cs"]),
    ("cshtml", &["*.cshtml"]),
    ("css", &["*.css", "*.scss"]),
    ("csv", &["*.csv"]),
    ("cuda", &["*.cu", "*.cuh"]),
    ("cython", &["*.pyx", "*.pxi", "*.pxd"]),
    ("d", &["*.d"]),
    ("dart", &["*.dart"]),
    ("dhall", &["*.dhall"]),
    ("diff", &["*.patch", "*.diff"]),
    ("docker", &["*Dockerfile*"]),
    ("dts", &["*.dts", "*.dtsi"]),
    ("dvc", &["Dvcfile", "*.dvc"]),
    ("ebuild", &["*.ebuild"]),
    ("edn", &["*.edn"]),
    ("elisp", &["*.el"]),
    ("elixir", &["*.ex", "*.eex", "*.exs"]),
    ("elm", &["*.elm"]),
    ("erb", &["*.erb"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fennel", &["*.fnl"]),
    ("fidl", &["*.fidl"]),
    ("fish", &["*.fish"]),
    ("flatbuffers", &["*.fbs"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("fut", &["*.fut"]),
    ("gap", &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
    ("gn", &["*.gn", "*.gni"]),
    ("go", &["*.go"]),
    ("gradle", &["*.gradle"]),
    ("groovy", &["*.groovy", "*.gradle"]),
    ("gzip", &["*.gz", "*.tgz"]),
    ("h", &["*.h", "*.hh", "*.hpp"]),
    ("haml", &["*.haml"]),
    ("hare", &["*.ha"]),
    ("haskell", &["*.hs", "*.lhs", "*.cpphs", "*.c2hs", "*.hsc"]),
    ("hbs", &["*.hbs"]),
    ("hs", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html", "*.ejs"]),
    ("hy", &["*.hy"]),
    ("idris", &["*.idr", "*.lidr"]),
    ("janet", &["*.janet"]),
    ("java", &["*.java", "*.jsp", "*.jspx", "*.properties"]),
    ("jinja", &["*.j2", "*.jinja", "*.jinja2"]),
    ("jl", &["*.jl"]),
    ("js", &["*.js", "*.jsx", "*.vue"]),
    ("json", &["*.json", "composer.lock"]),
    ("jsonl", &["*.jsonl"]),
    ("julia", &["*.jl"]),
    ("jupyter", &["*.ipynb", "*.jpynb"]),
    ("k", &["*.k"]),
    ("kotlin", &["*.kt", "*.kts"]),
    ("less", &["*.less"]),
    ("license", &[
        // General
        "COPYING", "COPYING[.-]*",
        "COPYRIGHT", "COPYRIGHT[.-]*",
        "EULA", "EULA[.-]*",
        "licen[cs]e", "licen[cs]e.*",
        "LICEN[CS]E", "LICEN[CS]E[.-]*", "*[.-]LICEN[CS]E*",
        "NOTICE", "NOTICE[.-]*",
        "PATENTS", "PATENTS[.-]*",
        "UNLICEN[CS]E", "UNLICEN[CS]E[.-]*",
        // GPL (gpl.txt, etc.)
        "agpl[.-]*",
        "gpl[.-]*",
        "lgpl[.-]*",
        // Other license-specific (APACHE-2.0.txt, etc.)
        "AGPL-*[0-9]*",
        "APACHE-*[0-9]*",
        "BSD-*[0-9]*",
        "CC-BY-*",
        "GFDL-*[0-9]*",
        "GNU-*[0-9]*",
        "GPL-*[0-9]*",
        "LGPL-*[0-9]*",
        "MIT-*[0-9]*",
        "MPL-*[0-9]*",
        "OFL-*[0-9]*",
    ]),
    ("lilypond", &["*.ly", "*.ily"]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lock", &["*.lock", "package-lock.json"]),
    ("log", &["*.log"]),
    ("lua", &["*.lua"]),
    ("lz4", &["*.lz4"]),
    ("lzma", &["*.lzma"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &[
        "[Gg][Nn][Uu]makefile", "[Mm]akefile",
        "[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
        "[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
        "*.mk", "*.mak"
    ]),
    ("mako", &["*.mako", "*.mao"]),
    ("man", &["*.[0-9lnpx]", "*.[0-9][cEFMmpSx]"]),
    ("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkd", "*.mkdn"]),
    ("matlab", &["*.m"]),
    ("md", &["*.markdown", "*.md", "*.mdown", "*.mkd", "*.mkdn"]),
    ("meson", &["meson.build", "meson_options.txt"]),
    ("minified", &["*.min.html", "*.min.css", "*.min.js"]),
    ("mint", &["*.mint"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("msbuild", &[
        "*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
    ]),
    ("nim", &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
    ("nix", &["*.nix"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("org", &["*.org", "*.org_archive"]),
    ("pants", &["BUILD"]),
    ("pascal", &["*.pas", "*.dpr", "*.lpr", "*.pp", "*.inc"]),
    ("pdf", &["*.pdf"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm", "*.t"]),
    ("php", &[
        // note that PHP 6 doesn't exist
        // See: https://wiki.php.net/rfc/php6
        "*.php", "*.php3", "*.php4", "*.php5", "*.php7", "*.php8",
        "*.pht", "*.phtml"
    ]),
    ("po", &["*.po"]),
    ("pod", &["*.pod"]),
    ("postscript", &["*.eps", "*.ps"]),
    ("protobuf", &["*.proto"]),
    ("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
    ("puppet", &["*.erb", "*.pp", "*.rb"]),
    ("purs", &["*.purs"]),
    ("py", &["*.py"]),
    ("qmake", &["*.pro", "*.pri", "*.prf"]),
    ("qml", &["*.qml"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("racket", &["*.rkt"]),
    ("rdoc", &["*.rdoc"]),
    ("readme", &["README*", "*README"]),
    ("red", &["*.r", "*.red", "*.reds"]),
    ("robot", &["*.robot"]),
    ("rst", &["*.rst"]),
    ("ruby", &[
        // Idiomatic files
        "config.ru", "Gemfile", ".irbrc", "Rakefile",
        // Extensions
        "*.gemspec", "*.rb", "*.rbw"
    ]),
    ("rust", &["*.rs"]),
    ("sass", &["*.sass", "*.scss"]),
    ("scala", &["*.scala", "*.sbt"]),
    ("sh", &[
        // Portable/misc. init files
        ".login", ".logout", ".profile", "profile",
        // bash-specific init files
        ".bash_login", "bash_login",
        ".bash_logout", "bash_logout",
        ".bash_profile", "bash_profile",
        ".bashrc", "bashrc", "*.bashrc",
        // csh-specific init files
        ".cshrc", "*.cshrc",
        // ksh-specific init files
        ".kshrc", "*.kshrc",
        // tcsh-specific init files
        ".tcshrc",
        // zsh-specific init files
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        // Extensions
        "*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
    ]),
    ("slim", &["*.skim", "*.slim", "*.slime"]),
    ("smarty", &["*.tpl"]),
    ("sml", &["*.sml", "*.sig"]),
    ("solidity", &["*.sol"]),
    ("soy", &["*.soy"]),
    ("spark", &["*.spark"]),
    ("spec", &["*.spec"]),
    ("sql", &["*.sql", "*.psql"]),
    ("stylus", &["*.styl"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("svg", &["*.svg"]),
    ("swift", &["*.swift"]),
    ("swig", &["*.def", "*.i"]),
    ("systemd", &[
        "*.automount", "*.conf", "*.device", "*.link", "*.mount", "*.path",
        "*.scope", "*.service", "*.slice", "*.socket", "*.swap", "*.target",
        "*.timer",
    ]),
    ("taskpaper", &["*.taskpaper"]),
    ("tcl", &["*.tcl"]),
    ("tex", &["*.tex", "*.ltx", "*.cls", "*.sty", "*.bib", "*.dtx", "*.ins"]),
    ("texinfo", &["*.texi"]),
    ("textile", &["*.textile"]),
    ("tf", &["*.tf"]),
    ("thrift", &["*.thrift"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("twig", &["*.twig"]),
    ("txt", &["*.txt"]),
    ("typoscript", &["*.typoscript", "*.ts"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vcl", &["*.vcl"]),
    ("verilog", &["*.v", "*.vh", "*.sv", "*.svh"]),
    ("vhdl", &["*.vhd", "*.vhdl"]),
    ("vim", &[
        "*.vim", ".vimrc", ".gvimrc", "vimrc", "gvimrc", "_vimrc", "_gvimrc",
    ]),
    ("vimscript", &[
        "*.vim", ".vimrc", ".gvimrc", "vimrc", "gvimrc", "_vimrc", "_gvimrc",
    ]),
    ("webidl", &["*.idl", "*.webidl", "*.widl"]),
    ("wiki", &["*.mediawiki", "*.wiki"]),
    ("xml", &[
        "*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
        "*.rng", "*.sch", "*.xhtml",
    ]),
    ("xz", &["*.xz", "*.txz"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
    ("yang", &["*.yang"]),
    ("z", &["*.Z"]),
    ("zig", &["*.zig"]),
    ("zsh", &[
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        "*.zsh",
    ]),
    ("zstd", &["*.zst", "*.zstd"]),
];
<|endoftext|>


<commit_before>//! An algorithm to find a path to refer to a certain item.

use crate::{
    db::DefDatabase,
    item_scope::ItemInNs,
    path::{ModPath, PathKind},
    ModuleId, ModuleDefId,
};
use hir_expand::name::Name;

// TODO don't import from super imports? or at least deprioritize
// TODO use super?
// TODO performance / memoize

pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
    // Base cases:

    // - if the item is already in scope, return the name under which it is
    let def_map = db.crate_def_map(from.krate);
    let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope;
    if let Some((name, _)) = from_scope.reverse_get(item) {
        return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
    }

    // - if the item is the crate root, return `crate`
    if item == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { krate: from.krate, local_id: def_map.root })) {
        return Some(ModPath::from_simple_segments(PathKind::Crate, Vec::new()));
    }

    // - if the item is the crate root of a dependency crate, return the name from the extern prelude
    for (name, def_id) in &def_map.extern_prelude {
        if item == ItemInNs::Types(*def_id) {
            return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
        }
    }

    // - if the item is in the prelude, return the name from there
    if let Some(prelude_module) = def_map.prelude {
        let prelude_def_map = db.crate_def_map(prelude_module.krate);
        let prelude_scope: &crate::item_scope::ItemScope = &prelude_def_map.modules[prelude_module.local_id].scope;
        if let Some((name, vis)) = prelude_scope.reverse_get(item) {
            if vis.is_visible_from(db, from) {
                return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
            }
        }
    }

    // Recursive case:
    // - if the item is an enum variant, refer to it via the enum
    if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
        if let Some(mut path) = find_path(db, ItemInNs::Types(variant.parent.into()), from) {
            let data = db.enum_data(variant.parent);
            path.segments.push(data.variants[variant.local_id].name.clone());
            return Some(path);
        }
        // If this doesn't work, it seems we have no way of referring to the
        // enum; that's very weird, but there might still be a reexport of the
        // variant somewhere
    }

    // - otherwise, look for modules containing (reexporting) it and import it from one of those
    let importable_locations = find_importable_locations(db, item, from);
    let mut candidate_paths = Vec::new();
    for (module_id, name) in importable_locations {
        // TODO prevent infinite loops
        let mut path = match find_path(db, ItemInNs::Types(ModuleDefId::ModuleId(module_id)), from) {
            None => continue,
            Some(path) => path,
        };
        path.segments.push(name);
        candidate_paths.push(path);
    }
    candidate_paths.into_iter().min_by_key(|path| path.segments.len())
}

fn find_importable_locations(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Vec<(ModuleId, Name)> {
    let crate_graph = db.crate_graph();
    let mut result = Vec::new();
    for krate in Some(from.krate).into_iter().chain(crate_graph.dependencies(from.krate).map(|dep| dep.crate_id)) {
        let def_map = db.crate_def_map(krate);
        for (local_id, data) in def_map.modules.iter() {
            if let Some((name, vis)) = data.scope.reverse_get(item) {
                if vis.is_visible_from(db, from) {
                    result.push((ModuleId { krate, local_id }, name.clone()));
                }
            }
        }
    }
    result
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_db::TestDB;
    use hir_expand::hygiene::Hygiene;
    use ra_db::fixture::WithFixture;
    use ra_syntax::ast::AstNode;

    /// `code` needs to contain a cursor marker; checks that `find_path` for the
    /// item the `path` refers to returns that same path when called from the
    /// module the cursor is in.
    fn check_found_path(code: &str, path: &str) {
        let (db, pos) = TestDB::with_position(code);
        let module = db.module_for_file(pos.file_id);
        let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
        let ast_path = parsed_path_file
            .syntax_node()
            .descendants()
            .find_map(ra_syntax::ast::Path::cast)
            .unwrap();
        let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();

        let crate_def_map = db.crate_def_map(module.krate);
        let resolved = crate_def_map
            .resolve_path(
                &db,
                module.local_id,
                &mod_path,
                crate::item_scope::BuiltinShadowMode::Module,
            )
            .0
            .take_types()
            .unwrap();

        let found_path = find_path(&db, ItemInNs::Types(resolved), module);

        assert_eq!(found_path, Some(mod_path));
    }

    #[test]
    fn same_module() {
        let code = r#"
            //- /main.rs
            struct S;
            <|>
        "#;
        check_found_path(code, "S");
    }

    #[test]
    fn enum_variant() {
        let code = r#"
            //- /main.rs
            enum E { A }
            <|>
        "#;
        check_found_path(code, "E::A");
    }

    #[test]
    fn sub_module() {
        let code = r#"
            //- /main.rs
            mod foo {
                pub struct S;
            }
            <|>
        "#;
        check_found_path(code, "foo::S");
    }

    #[test]
    fn crate_root() {
        let code = r#"
            //- /main.rs
            mod foo;
            //- /foo.rs
            <|>
        "#;
        check_found_path(code, "crate");
    }

    #[test]
    fn same_crate() {
        let code = r#"
            //- /main.rs
            mod foo;
            struct S;
            //- /foo.rs
            <|>
        "#;
        check_found_path(code, "crate::S");
    }

    #[test]
    fn different_crate() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std
            pub struct S;
        "#;
        check_found_path(code, "std::S");
    }

    #[test]
    fn different_crate_renamed() {
        let code = r#"
            //- /main.rs crate:main deps:std
            extern crate std as std_renamed;
            <|>
            //- /std.rs crate:std
            pub struct S;
        "#;
        check_found_path(code, "std_renamed::S");
    }

    #[test]
    fn same_crate_reexport() {
        let code = r#"
            //- /main.rs
            mod bar {
                mod foo { pub(super) struct S; }
                pub(crate) use foo::*;
            }
            <|>
        "#;
        check_found_path(code, "bar::S");
    }

    #[test]
    fn same_crate_reexport_rename() {
        let code = r#"
            //- /main.rs
            mod bar {
                mod foo { pub(super) struct S; }
                pub(crate) use foo::S as U;
            }
            <|>
        "#;
        check_found_path(code, "bar::U");
    }

    #[test]
    fn different_crate_reexport() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std deps:core
            pub use core::S;
            //- /core.rs crate:core
            pub struct S;
        "#;
        check_found_path(code, "std::S");
    }

    #[test]
    fn prelude() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std
            pub mod prelude { pub struct S; }
            #[prelude_import]
            pub use prelude::*;
        "#;
        check_found_path(code, "S");
    }

    #[test]
    fn enum_variant_from_prelude() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std
            pub mod prelude {
                pub enum Option<T> { Some(T), None }
                pub use Option::*;
            }
            #[prelude_import]
            pub use prelude::*;
        "#;
        check_found_path(code, "None");
        check_found_path(code, "Some");
    }

    #[test]
    fn shortest_path() {
        let code = r#"
            //- /main.rs
            pub mod foo;
            pub mod baz;
            struct S;
            <|>
            //- /foo.rs
            pub mod bar { pub struct S; }
            //- /baz.rs
            pub use crate::foo::bar::S;
        "#;
        check_found_path(code, "baz::S");
    }
}
<commit_msg>Use super, don't use private imports<commit_after>//! An algorithm to find a path to refer to a certain item.

use crate::{
    db::DefDatabase,
    item_scope::ItemInNs,
    path::{ModPath, PathKind},
    ModuleId, ModuleDefId,
};
use hir_expand::name::Name;

// TODO performance / memoize

pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
    // Base cases:

    // - if the item is already in scope, return the name under which it is
    let def_map = db.crate_def_map(from.krate);
    let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope;
    if let Some((name, _)) = from_scope.reverse_get(item) {
        return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
    }

    // - if the item is the crate root, return `crate`
    if item == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { krate: from.krate, local_id: def_map.root })) {
        return Some(ModPath::from_simple_segments(PathKind::Crate, Vec::new()));
    }

    // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
    if let Some(parent_id) = def_map.modules[from.local_id].parent {
        if item == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { krate: from.krate, local_id: parent_id })) {
            return Some(ModPath::from_simple_segments(PathKind::Super(1), Vec::new()));
        }
    }

    // - if the item is the crate root of a dependency crate, return the name from the extern prelude
    for (name, def_id) in &def_map.extern_prelude {
        if item == ItemInNs::Types(*def_id) {
            return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
        }
    }

    // - if the item is in the prelude, return the name from there
    if let Some(prelude_module) = def_map.prelude {
        let prelude_def_map = db.crate_def_map(prelude_module.krate);
        let prelude_scope: &crate::item_scope::ItemScope = &prelude_def_map.modules[prelude_module.local_id].scope;
        if let Some((name, vis)) = prelude_scope.reverse_get(item) {
            if vis.is_visible_from(db, from) {
                return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
            }
        }
    }

    // Recursive case:
    // - if the item is an enum variant, refer to it via the enum
    if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
        if let Some(mut path) = find_path(db, ItemInNs::Types(variant.parent.into()), from) {
            let data = db.enum_data(variant.parent);
            path.segments.push(data.variants[variant.local_id].name.clone());
            return Some(path);
        }
        // If this doesn't work, it seems we have no way of referring to the
        // enum; that's very weird, but there might still be a reexport of the
        // variant somewhere
    }

    // - otherwise, look for modules containing (reexporting) it and import it from one of those
    let importable_locations = find_importable_locations(db, item, from);
    let mut candidate_paths = Vec::new();
    for (module_id, name) in importable_locations {
        // TODO prevent infinite loops
        let mut path = match find_path(db, ItemInNs::Types(ModuleDefId::ModuleId(module_id)), from) {
            None => continue,
            Some(path) => path,
        };
        path.segments.push(name);
        candidate_paths.push(path);
    }
    candidate_paths.into_iter().min_by_key(|path| path.segments.len())
}

fn find_importable_locations(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Vec<(ModuleId, Name)> {
    let crate_graph = db.crate_graph();
    let mut result = Vec::new();
    for krate in Some(from.krate).into_iter().chain(crate_graph.dependencies(from.krate).map(|dep| dep.crate_id)) {
        let def_map = db.crate_def_map(krate);
        for (local_id, data) in def_map.modules.iter() {
            if let Some((name, vis)) = data.scope.reverse_get(item) {
                let is_private = if let crate::visibility::Visibility::Module(private_to) = vis {
                    private_to.local_id == local_id
                } else { false };
                let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
                    data.scope.declarations().any(|it| it == module_def_id)
                } else { false };
                if is_private && !is_original_def {
                    // Ignore private imports. these could be used if we are
                    // in a submodule of this module, but that's usually not
                    // what the user wants; and if this module can import
                    // the item and we're a submodule of it, so can we.
                    continue;
                }
                if vis.is_visible_from(db, from) {
                    result.push((ModuleId { krate, local_id }, name.clone()));
                }
            }
        }
    }
    result
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_db::TestDB;
    use hir_expand::hygiene::Hygiene;
    use ra_db::fixture::WithFixture;
    use ra_syntax::ast::AstNode;

    /// `code` needs to contain a cursor marker; checks that `find_path` for the
    /// item the `path` refers to returns that same path when called from the
    /// module the cursor is in.
    fn check_found_path(code: &str, path: &str) {
        let (db, pos) = TestDB::with_position(code);
        let module = db.module_for_file(pos.file_id);
        let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
        let ast_path = parsed_path_file
            .syntax_node()
            .descendants()
            .find_map(ra_syntax::ast::Path::cast)
            .unwrap();
        let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();

        let crate_def_map = db.crate_def_map(module.krate);
        let resolved = crate_def_map
            .resolve_path(
                &db,
                module.local_id,
                &mod_path,
                crate::item_scope::BuiltinShadowMode::Module,
            )
            .0
            .take_types()
            .unwrap();

        let found_path = find_path(&db, ItemInNs::Types(resolved), module);

        assert_eq!(found_path, Some(mod_path));
    }

    #[test]
    fn same_module() {
        let code = r#"
            //- /main.rs
            struct S;
            <|>
        "#;
        check_found_path(code, "S");
    }

    #[test]
    fn enum_variant() {
        let code = r#"
            //- /main.rs
            enum E { A }
            <|>
        "#;
        check_found_path(code, "E::A");
    }

    #[test]
    fn sub_module() {
        let code = r#"
            //- /main.rs
            mod foo {
                pub struct S;
            }
            <|>
        "#;
        check_found_path(code, "foo::S");
    }

    #[test]
    fn super_module() {
        let code = r#"
            //- /main.rs
            mod foo;
            //- /foo.rs
            mod bar;
            struct S;
            //- /foo/bar.rs
            <|>
        "#;
        check_found_path(code, "super::S");
    }

    #[test]
    fn crate_root() {
        let code = r#"
            //- /main.rs
            mod foo;
            //- /foo.rs
            <|>
        "#;
        check_found_path(code, "crate");
    }

    #[test]
    fn same_crate() {
        let code = r#"
            //- /main.rs
            mod foo;
            struct S;
            //- /foo.rs
            <|>
        "#;
        check_found_path(code, "crate::S");
    }

    #[test]
    fn different_crate() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std
            pub struct S;
        "#;
        check_found_path(code, "std::S");
    }

    #[test]
    fn different_crate_renamed() {
        let code = r#"
            //- /main.rs crate:main deps:std
            extern crate std as std_renamed;
            <|>
            //- /std.rs crate:std
            pub struct S;
        "#;
        check_found_path(code, "std_renamed::S");
    }

    #[test]
    fn same_crate_reexport() {
        let code = r#"
            //- /main.rs
            mod bar {
                mod foo { pub(super) struct S; }
                pub(crate) use foo::*;
            }
            <|>
        "#;
        check_found_path(code, "bar::S");
    }

    #[test]
    fn same_crate_reexport_rename() {
        let code = r#"
            //- /main.rs
            mod bar {
                mod foo { pub(super) struct S; }
                pub(crate) use foo::S as U;
            }
            <|>
        "#;
        check_found_path(code, "bar::U");
    }

    #[test]
    fn different_crate_reexport() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std deps:core
            pub use core::S;
            //- /core.rs crate:core
            pub struct S;
        "#;
        check_found_path(code, "std::S");
    }

    #[test]
    fn prelude() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std
            pub mod prelude { pub struct S; }
            #[prelude_import]
            pub use prelude::*;
        "#;
        check_found_path(code, "S");
    }

    #[test]
    fn enum_variant_from_prelude() {
        let code = r#"
            //- /main.rs crate:main deps:std
            <|>
            //- /std.rs crate:std
            pub mod prelude {
                pub enum Option<T> { Some(T), None }
                pub use Option::*;
            }
            #[prelude_import]
            pub use prelude::*;
        "#;
        check_found_path(code, "None");
        check_found_path(code, "Some");
    }

    #[test]
    fn shortest_path() {
        let code = r#"
            //- /main.rs
            pub mod foo;
            pub mod baz;
            struct S;
            <|>
            //- /foo.rs
            pub mod bar { pub struct S; }
            //- /baz.rs
            pub use crate::foo::bar::S;
        "#;
        check_found_path(code, "baz::S");
    }

    #[test]
    fn discount_private_imports() {
        let code = r#"
            //- /main.rs
            mod foo;
            pub mod bar { pub struct S; }
            use bar::S;
            //- /foo.rs
            <|>
        "#;
        // crate::S would be shorter, but using private imports seems wrong
        check_found_path(code, "crate::bar::S");
    }
}
<|endoftext|>


<commit_before>use std::cmp::{max, min};

use ra_db::SyntaxDatabase;
use ra_syntax::{
    AstNode, SyntaxNode, TextUnit, TextRange,
    SyntaxKind::FN_DEF,
    ast::{self, ArgListOwner, DocCommentsOwner},
    algo::find_node_at_offset,
};

use crate::{FilePosition, CallInfo, db::RootDatabase};

/// Computes parameter information for the given call expression.
pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
    let file = db.source_file(position.file_id);
    let syntax = file.syntax();

    // Find the calling expression and it's NameRef
    let calling_node = FnCallNode::with_node(syntax, position.offset)?;
    let name_ref = calling_node.name_ref()?;

    // Resolve the function's NameRef (NOTE: this isn't entirely accurate).
    let file_symbols = db.index_resolve(name_ref);
    let symbol = file_symbols
        .into_iter()
        .find(|it| it.ptr.kind() == FN_DEF)?;
    let fn_file = db.source_file(symbol.file_id);
    let fn_def = symbol.ptr.resolve(&fn_file);
    let fn_def = ast::FnDef::cast(&fn_def).unwrap();
    let mut call_info = CallInfo::new(fn_def)?;
    // If we have a calling expression let's find which argument we are on
    let num_params = call_info.parameters.len();
    let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();

    if num_params == 1 {
        if !has_self {
            call_info.active_parameter = Some(0);
        }
    } else if num_params > 1 {
        // Count how many parameters into the call we are.
        // TODO: This is best effort for now and should be fixed at some point.
        // It may be better to see where we are in the arg_list and then check
        // where offset is in that list (or beyond).
        // Revisit this after we get documentation comments in.
        if let Some(ref arg_list) = calling_node.arg_list() {
            let start = arg_list.syntax().range().start();

            let range_search = TextRange::from_to(start, position.offset);
            let mut commas: usize = arg_list
                .syntax()
                .text()
                .slice(range_search)
                .to_string()
                .matches(',')
                .count();

            // If we have a method call eat the first param since it's just self.
            if has_self {
                commas += 1;
            }

            call_info.active_parameter = Some(commas);
        }
    }

    Some(call_info)
}

enum FnCallNode<'a> {
    CallExpr(&'a ast::CallExpr),
    MethodCallExpr(&'a ast::MethodCallExpr),
}

impl<'a> FnCallNode<'a> {
    pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> {
        if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
            return Some(FnCallNode::CallExpr(expr));
        }
        if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) {
            return Some(FnCallNode::MethodCallExpr(expr));
        }
        None
    }

    pub fn name_ref(&self) -> Option<&'a ast::NameRef> {
        match *self {
            FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() {
                ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
                _ => return None,
            }),

            FnCallNode::MethodCallExpr(call_expr) => call_expr
                .syntax()
                .children()
                .filter_map(ast::NameRef::cast)
                .nth(0),
        }
    }

    pub fn arg_list(&self) -> Option<&'a ast::ArgList> {
        match *self {
            FnCallNode::CallExpr(expr) => expr.arg_list(),
            FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
        }
    }
}

impl CallInfo {
    fn new(node: &ast::FnDef) -> Option<Self> {
        let mut doc = None;

        // Strip the body out for the label.
        let mut label: String = if let Some(body) = node.body() {
            let body_range = body.syntax().range();
            let label: String = node
                .syntax()
                .children()
                .filter(|child| !child.range().is_subrange(&body_range))
                .map(|node| node.text().to_string())
                .collect();
            label
        } else {
            node.syntax().text().to_string()
        };

        if let Some((comment_range, docs)) = extract_doc_comments(node) {
            let comment_range = comment_range
                .checked_sub(node.syntax().range().start())
                .unwrap();
            let start = comment_range.start().to_usize();
            let end = comment_range.end().to_usize();

            // Remove the comment from the label
            label.replace_range(start..end, "");

            // Massage markdown
            let mut processed_lines = Vec::new();
            let mut in_code_block = false;
            for line in docs.lines() {
                if line.starts_with("```") {
                    in_code_block = !in_code_block;
                }

                let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
                    "```rust".into()
                } else {
                    line.to_string()
                };

                processed_lines.push(line);
            }

            if !processed_lines.is_empty() {
                doc = Some(processed_lines.join("\n"));
            }
        }

        Some(CallInfo {
            parameters: param_list(node),
            label: label.trim().to_owned(),
            doc,
            active_parameter: None,
        })
    }
}

fn extract_doc_comments(node: &ast::FnDef) -> Option<(TextRange, String)> {
    if node.doc_comments().count() == 0 {
        return None;
    }

    let comment_text = node.doc_comment_text();

    let (begin, end) = node
        .doc_comments()
        .map(|comment| comment.syntax().range())
        .map(|range| (range.start().to_usize(), range.end().to_usize()))
        .fold((std::usize::MAX, std::usize::MIN), |acc, range| {
            (min(acc.0, range.0), max(acc.1, range.1))
        });

    let range = TextRange::from_to(TextUnit::from_usize(begin), TextUnit::from_usize(end));

    Some((range, comment_text))
}

fn param_list(node: &ast::FnDef) -> Vec<String> {
    let mut res = vec![];
    if let Some(param_list) = node.param_list() {
        if let Some(self_param) = param_list.self_param() {
            res.push(self_param.syntax().text().to_string())
        }

        // Maybe use param.pat here? See if we can just extract the name?
        //res.extend(param_list.params().map(|p| p.syntax().text().to_string()));
        res.extend(
            param_list
                .params()
                .filter_map(|p| p.pat())
                .map(|pat| pat.syntax().text().to_string()),
        );
    }
    res
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::mock_analysis::single_file_with_position;

    fn call_info(text: &str) -> CallInfo {
        let (analysis, position) = single_file_with_position(text);
        analysis.call_info(position).unwrap().unwrap()
    }

    #[test]
    fn test_fn_signature_two_args_first() {
        let info = call_info(
            r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(<|>3, ); }"#,
        );

        assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string()));
        assert_eq!(info.active_parameter, Some(0));
    }

    #[test]
    fn test_fn_signature_two_args_second() {
        let info = call_info(
            r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3, <|>); }"#,
        );

        assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string()));
        assert_eq!(info.active_parameter, Some(1));
    }

    #[test]
    fn test_fn_signature_for_impl() {
        let info = call_info(
            r#"struct F; impl F { pub fn new() { F{}} }
fn bar() {let _ : F = F::new(<|>);}"#,
        );

        assert_eq!(info.parameters, Vec::<String>::new());
        assert_eq!(info.active_parameter, None);
    }

    #[test]
    fn test_fn_signature_for_method_self() {
        let info = call_info(
            r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["&self".to_string()]);
        assert_eq!(info.active_parameter, None);
    }

    #[test]
    fn test_fn_signature_for_method_with_arg() {
        let info = call_info(
            r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self, x: i32) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["&self".to_string(), "x".to_string()]);
        assert_eq!(info.active_parameter, Some(1));
    }

    #[test]
    fn test_fn_signature_with_docs_simple() {
        let info = call_info(
            r#"
/// test
// non-doc-comment
fn foo(j: u32) -> u32 {
    j
}

fn bar() {
    let _ = foo(<|>);
}
"#,
        );

        assert_eq!(info.parameters, vec!["j".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string());
        assert_eq!(info.doc, Some("test".into()));
    }

    #[test]
    fn test_fn_signature_with_docs() {
        let info = call_info(
            r#"
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, my_crate::add_one(5));
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

pub fn do() {
    add_one(<|>
}"#,
        );

        assert_eq!(info.parameters, vec!["x".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
        assert_eq!(
            info.doc,
            Some(
                r#"Adds one to the number given.

# Examples

```rust
let five = 5;

assert_eq!(6, my_crate::add_one(5));
```"#
                    .into()
            )
        );
    }

    #[test]
    fn test_fn_signature_with_docs_impl() {
        let info = call_info(
            r#"
struct addr;
impl addr {
    /// Adds one to the number given.
    ///
    /// # Examples
    ///
    /// ```
    /// let five = 5;
    ///
    /// assert_eq!(6, my_crate::add_one(5));
    /// ```
    pub fn add_one(x: i32) -> i32 {
        x + 1
    }
}

pub fn do_it() {
    addr {};
    addr::add_one(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["x".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
        assert_eq!(
            info.doc,
            Some(
                r#"Adds one to the number given.

# Examples

```rust
let five = 5;

assert_eq!(6, my_crate::add_one(5));
```"#
                    .into()
            )
        );
    }

    #[test]
    fn test_fn_signature_with_docs_from_actix() {
        let info = call_info(
            r#"
pub trait WriteHandler<E>
where
    Self: Actor,
    Self::Context: ActorContext,
{
    /// Method is called when writer emits error.
    ///
    /// If this method returns `ErrorAction::Continue` writer processing
    /// continues otherwise stream processing stops.
    fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
        Running::Stop
    }

    /// Method is called when writer finishes.
    ///
    /// By default this method stops actor's `Context`.
    fn finished(&mut self, ctx: &mut Self::Context) {
        ctx.stop()
    }
}

pub fn foo() {
    WriteHandler r;
    r.finished(<|>);
}

"#,
        );

        assert_eq!(
            info.parameters,
            vec!["&mut self".to_string(), "ctx".to_string()]
        );
        assert_eq!(info.active_parameter, Some(1));
        assert_eq!(
            info.doc,
            Some(
                r#"Method is called when writer finishes.

By default this method stops actor's `Context`."#
                    .into()
            )
        );
    }

}
<commit_msg>Simplify CallInfo label and documentation<commit_after>use ra_db::SyntaxDatabase;
use ra_syntax::{
    AstNode, SyntaxNode, TextUnit, TextRange,
    SyntaxKind::FN_DEF,
    ast::{self, ArgListOwner, DocCommentsOwner},
    algo::find_node_at_offset,
};

use crate::{FilePosition, CallInfo, db::RootDatabase};

/// Computes parameter information for the given call expression.
pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
    let file = db.source_file(position.file_id);
    let syntax = file.syntax();

    // Find the calling expression and it's NameRef
    let calling_node = FnCallNode::with_node(syntax, position.offset)?;
    let name_ref = calling_node.name_ref()?;

    // Resolve the function's NameRef (NOTE: this isn't entirely accurate).
    let file_symbols = db.index_resolve(name_ref);
    let symbol = file_symbols
        .into_iter()
        .find(|it| it.ptr.kind() == FN_DEF)?;
    let fn_file = db.source_file(symbol.file_id);
    let fn_def = symbol.ptr.resolve(&fn_file);
    let fn_def = ast::FnDef::cast(&fn_def).unwrap();
    let mut call_info = CallInfo::new(fn_def)?;
    // If we have a calling expression let's find which argument we are on
    let num_params = call_info.parameters.len();
    let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();

    if num_params == 1 {
        if !has_self {
            call_info.active_parameter = Some(0);
        }
    } else if num_params > 1 {
        // Count how many parameters into the call we are.
        // TODO: This is best effort for now and should be fixed at some point.
        // It may be better to see where we are in the arg_list and then check
        // where offset is in that list (or beyond).
        // Revisit this after we get documentation comments in.
        if let Some(ref arg_list) = calling_node.arg_list() {
            let start = arg_list.syntax().range().start();

            let range_search = TextRange::from_to(start, position.offset);
            let mut commas: usize = arg_list
                .syntax()
                .text()
                .slice(range_search)
                .to_string()
                .matches(',')
                .count();

            // If we have a method call eat the first param since it's just self.
            if has_self {
                commas += 1;
            }

            call_info.active_parameter = Some(commas);
        }
    }

    Some(call_info)
}

enum FnCallNode<'a> {
    CallExpr(&'a ast::CallExpr),
    MethodCallExpr(&'a ast::MethodCallExpr),
}

impl<'a> FnCallNode<'a> {
    pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> {
        if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
            return Some(FnCallNode::CallExpr(expr));
        }
        if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) {
            return Some(FnCallNode::MethodCallExpr(expr));
        }
        None
    }

    pub fn name_ref(&self) -> Option<&'a ast::NameRef> {
        match *self {
            FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() {
                ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
                _ => return None,
            }),

            FnCallNode::MethodCallExpr(call_expr) => call_expr
                .syntax()
                .children()
                .filter_map(ast::NameRef::cast)
                .nth(0),
        }
    }

    pub fn arg_list(&self) -> Option<&'a ast::ArgList> {
        match *self {
            FnCallNode::CallExpr(expr) => expr.arg_list(),
            FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
        }
    }
}

impl CallInfo {
    fn new(node: &ast::FnDef) -> Option<Self> {
        let label: String = if let Some(body) = node.body() {
            let body_range = body.syntax().range();
            let label: String = node
                .syntax()
                .children()
                .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body
                .filter(|child| ast::Comment::cast(child).is_none()) // Filter out doc comments
                .map(|node| node.text().to_string())
                .collect();
            label
        } else {
            node.syntax().text().to_string()
        };

        let mut doc = None;
        let docs = node.doc_comment_text();
        if !docs.is_empty() {
            // Massage markdown
            let mut processed_lines = Vec::new();
            let mut in_code_block = false;
            for line in docs.lines() {
                if line.starts_with("```") {
                    in_code_block = !in_code_block;
                }

                let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
                    "```rust".into()
                } else {
                    line.to_string()
                };

                processed_lines.push(line);
            }

            doc = Some(processed_lines.join("\n"));
        }

        Some(CallInfo {
            parameters: param_list(node),
            label: label.trim().to_owned(),
            doc,
            active_parameter: None,
        })
    }
}

fn param_list(node: &ast::FnDef) -> Vec<String> {
    let mut res = vec![];
    if let Some(param_list) = node.param_list() {
        if let Some(self_param) = param_list.self_param() {
            res.push(self_param.syntax().text().to_string())
        }

        // Maybe use param.pat here? See if we can just extract the name?
        //res.extend(param_list.params().map(|p| p.syntax().text().to_string()));
        res.extend(
            param_list
                .params()
                .filter_map(|p| p.pat())
                .map(|pat| pat.syntax().text().to_string()),
        );
    }
    res
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::mock_analysis::single_file_with_position;

    fn call_info(text: &str) -> CallInfo {
        let (analysis, position) = single_file_with_position(text);
        analysis.call_info(position).unwrap().unwrap()
    }

    #[test]
    fn test_fn_signature_two_args_first() {
        let info = call_info(
            r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(<|>3, ); }"#,
        );

        assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string()));
        assert_eq!(info.active_parameter, Some(0));
    }

    #[test]
    fn test_fn_signature_two_args_second() {
        let info = call_info(
            r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3, <|>); }"#,
        );

        assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string()));
        assert_eq!(info.active_parameter, Some(1));
    }

    #[test]
    fn test_fn_signature_for_impl() {
        let info = call_info(
            r#"struct F; impl F { pub fn new() { F{}} }
fn bar() {let _ : F = F::new(<|>);}"#,
        );

        assert_eq!(info.parameters, Vec::<String>::new());
        assert_eq!(info.active_parameter, None);
    }

    #[test]
    fn test_fn_signature_for_method_self() {
        let info = call_info(
            r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["&self".to_string()]);
        assert_eq!(info.active_parameter, None);
    }

    #[test]
    fn test_fn_signature_for_method_with_arg() {
        let info = call_info(
            r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self, x: i32) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["&self".to_string(), "x".to_string()]);
        assert_eq!(info.active_parameter, Some(1));
    }

    #[test]
    fn test_fn_signature_with_docs_simple() {
        let info = call_info(
            r#"
/// test
// non-doc-comment
fn foo(j: u32) -> u32 {
    j
}

fn bar() {
    let _ = foo(<|>);
}
"#,
        );

        assert_eq!(info.parameters, vec!["j".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string());
        assert_eq!(info.doc, Some("test".into()));
    }

    #[test]
    fn test_fn_signature_with_docs() {
        let info = call_info(
            r#"
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, my_crate::add_one(5));
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

pub fn do() {
    add_one(<|>
}"#,
        );

        assert_eq!(info.parameters, vec!["x".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
        assert_eq!(
            info.doc,
            Some(
                r#"Adds one to the number given.

# Examples

```rust
let five = 5;

assert_eq!(6, my_crate::add_one(5));
```"#
                    .into()
            )
        );
    }

    #[test]
    fn test_fn_signature_with_docs_impl() {
        let info = call_info(
            r#"
struct addr;
impl addr {
    /// Adds one to the number given.
    ///
    /// # Examples
    ///
    /// ```
    /// let five = 5;
    ///
    /// assert_eq!(6, my_crate::add_one(5));
    /// ```
    pub fn add_one(x: i32) -> i32 {
        x + 1
    }
}

pub fn do_it() {
    addr {};
    addr::add_one(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["x".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
        assert_eq!(
            info.doc,
            Some(
                r#"Adds one to the number given.

# Examples

```rust
let five = 5;

assert_eq!(6, my_crate::add_one(5));
```"#
                    .into()
            )
        );
    }

    #[test]
    fn test_fn_signature_with_docs_from_actix() {
        let info = call_info(
            r#"
pub trait WriteHandler<E>
where
    Self: Actor,
    Self::Context: ActorContext,
{
    /// Method is called when writer emits error.
    ///
    /// If this method returns `ErrorAction::Continue` writer processing
    /// continues otherwise stream processing stops.
    fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
        Running::Stop
    }

    /// Method is called when writer finishes.
    ///
    /// By default this method stops actor's `Context`.
    fn finished(&mut self, ctx: &mut Self::Context) {
        ctx.stop()
    }
}

pub fn foo() {
    WriteHandler r;
    r.finished(<|>);
}

"#,
        );

        assert_eq!(
            info.parameters,
            vec!["&mut self".to_string(), "ctx".to_string()]
        );
        assert_eq!(info.active_parameter, Some(1));
        assert_eq!(
            info.doc,
            Some(
                r#"Method is called when writer finishes.

By default this method stops actor's `Context`."#
                    .into()
            )
        );
    }

}
<|endoftext|>


<commit_before>use ast::*;
use binding_map::BindingMap;
use closure::Closure;
use compiler::*;
use grammar::*;
use instructions::*;
use native::find_lib;
use native::FileDescriptorMap;
use std::fs;
use std::fs::File;
use std::io::Write;
use value::Value;

use exception_handler::ExceptionHandler;

use std::cell::RefCell;
use std::rc::Rc;

#[derive(Clone, Eq, Debug, PartialEq)]
struct Frame {
    bindings: BindingMap,
    exception_handlers: Vec<ExceptionHandler>,
}

impl Frame {
    pub fn new(bindings: BindingMap) -> Frame {
        Frame {
            bindings: bindings,
            exception_handlers: Vec::new(),
        }
    }
}

#[derive(Clone, Eq, Debug, PartialEq)]
pub struct Vm {
    instructions: Rc<InstructionSequence>,
    pc: usize,
    stack: Vec<Value>,
    frames: Vec<Frame>,
    pub file_descriptors: FileDescriptorMap,
}

impl Vm {
    pub fn new(source: &str) -> Vm {
        let stmts = statements(source);
        let instructions = compile(&stmts.unwrap());
        let map = BindingMap::new(None);
        let frame = Frame::new(map);

        let vm = Vm {
            instructions: Rc::new(instructions),
            pc: 0,
            stack: Vec::new(),
            frames: vec![frame],
            file_descriptors: FileDescriptorMap::new(),
        };
        vm
    }

    pub fn empty() -> Vm {
        let map = BindingMap::new(None);
        let frame = Frame::new(map);
        let vm = Vm {
            instructions: Rc::new(vec![]),
            pc: 0,
            stack: Vec::new(),
            frames: vec![frame],
            file_descriptors: FileDescriptorMap::new(),
        };
        vm
    }

    pub fn run<'b>(&'b mut self) {
        loop {
            let insn_result = Vm::next_instruction(self);
            let instruction;

            instruction = if let Some(i) = insn_result {
                trace!("next instruction: {:?}", i);
                i
            } else {
                trace!("instruction {:?} not found, terminating", insn_result);
                break;
            };

            match instruction {
                Instruction::Clear => self.stack.clear(),
                Instruction::Push(ref value) => {
                    let top_bindings = &mut self.frames.last_mut().unwrap().bindings;
                    self.stack.push(Vm::literal_to_value(value, top_bindings))
                }
                Instruction::Assign(ref binding_name) => {
                    let value = self.stack.pop().unwrap();
                    self.frames
                        .last_mut()
                        .unwrap()
                        .bindings
                        .assign(binding_name, value)
                }
                Instruction::LocalAssign(ref binding_name) => {
                    let value = self.stack.pop().unwrap();
                    self.frames
                        .last_mut()
                        .unwrap()
                        .bindings
                        .local_assign(binding_name, value)
                }
                Instruction::Call(arg_size) => {
                    let closure_info = match self.stack.pop() {
                        Some(Value::Closure(arg_names, closure)) => Ok((arg_names, closure)),
                        Some(x) => Err(format!("expected a closure, got {:?}", x)),
                        None => Err(format!("expected a closure, got None")),
                    };

                    let new_stack_length = { self.stack.len() - arg_size };
                    let mut args = self.stack.split_off(new_stack_length);
                    let (closure_args, closure) = match closure_info {
                        Ok(info) => info,
                        Err(m) => panic!(m),
                    };
                    if arg_size != closure_args.len() {
                        panic!(
                            "wrong number of arguments, expected {:?}, got {:?}",
                            closure_args.len(),
                            arg_size
                        )
                    };
                    let local_bindings = (*closure_args)
                        .clone()
                        .into_iter()
                        .rev()
                        .map(|arg_name| (arg_name, args.pop().unwrap()))
                        .collect();

                    self.reset_instructions(
                        closure.instructions.clone(),
                        Some(closure.init_map(local_bindings)),
                    );
                }
                Instruction::Fetch(ref binding_name) => {
                    let value = self.fetch(binding_name).unwrap();
                    self.stack.push(value);
                }
                Instruction::MakeMap(size) => {
                    let map = (0..size)
                        .into_iter()
                        .map(|_| {
                            let value = self.stack.pop().unwrap();
                            let key = self.stack.pop().unwrap();
                            (key, value)
                        })
                        .collect();
                    self.stack.push(Value::Map(Rc::new(RefCell::new(map))))
                }
                Instruction::Rescue(ref pattern, ref iseq) => {
                    let top_bindings = { &mut self.frames.last_mut().unwrap().bindings.clone() };
                    let closure = Closure::new(iseq.clone(), top_bindings);
                    self.frames
                        .last_mut()
                        .unwrap()
                        .exception_handlers
                        .push(ExceptionHandler::new(pattern.clone(), closure))
                }
                Instruction::Raise => {
                    let raised_value = self.stack.pop().unwrap();
                    self.raise(raised_value);
                }
                Instruction::BinOp(op) => {
                    let right = self.stack.pop().unwrap();
                    let left = self.stack.pop().unwrap();

                    let binop_result = match op {
                        Op::Add => left.add(right),
                        Op::Sub => left.sub(right),
                        Op::Mul => left.mul(right),
                        Op::Div => left.div(right),
                    };

                    if let Ok(result) = binop_result {
                        self.stack.push(result);
                    } else {
                        // TODO: Raise
                    }
                }
                Instruction::IndexAccess => {
                    let property = self.stack.pop().unwrap();
                    let target = self.stack.pop().unwrap();

                    match target {
                        Value::Map(ref map) => {
                            if let Some(value) = map.borrow().get(&property) {
                                self.stack.push((*value).clone());
                            } else {
                                panic!("no value for {:?}", target); // TODO: Raise
                            }
                        }
                        v => panic!("can't use index access for {:?}", v), // TODO: Raise
                    };
                }
                Instruction::IndexAssign => {
                    let value = self.stack.pop().unwrap();
                    let property = self.stack.pop().unwrap();
                    let mut target = self.stack.pop().unwrap();

                    match target {
                        Value::Map(ref mut map) => {
                            map.borrow_mut().insert(property, value);
                        }
                        v => panic!("can't use index access for {:?}", v), // TODO: Raise
                    };
                }
                Instruction::Import => {
                    let name = self.stack.pop().unwrap();
                    if let Value::CharString(ref str) = name {
                        if let Some(lib) = find_lib(str) {
                            self.stack.push(lib.clone());
                        }
                    } else {
                        panic!("import value must be a string"); // TODO: Raise
                    }
                }
                Instruction::Native(native_fn) => {
                    trace!("Starting native code");
                    let instructions = native_fn.call(self);
                    trace!("Finished native code");
                    self.reset_instructions(Rc::new(instructions), None)
                }
                _ => panic!("unknown instruction {:?}", instruction),
            };
        }
    }

    pub fn push(&mut self, value: Value) {
        self.stack.push(value);
    }

    pub fn pop(&mut self) -> Option<Value> {
        self.stack.pop()
    }

    pub fn fetch(&mut self, name: &String) -> Option<Value> {
        self.bindings().fetch(name)
    }

    pub fn local_assign(&mut self, name: &String, value: Value) {
        self.bindings().local_assign(name, value);
    }

    fn bindings(&mut self) -> &mut BindingMap {
        &mut self.frames.last_mut().unwrap().bindings
    }

    fn raise(&mut self, value: Value) {
        let matched_handler = self
            .frames
            .iter()
            .rev()
            .filter_map(|frame| {
                let handlers = frame
                    .exception_handlers
                    .iter()
                    .filter_map(|handler| match handler.matches(value.clone()) {
                        Some(bindings) => Some((handler, bindings)),
                        None => None,
                    })
                    .collect::<Vec<_>>();

                if handlers.is_empty() {
                    return None;
                }

                trace!("found handlers: {:?}", handlers.len());
                Some(handlers.first().unwrap().clone())
            })
            .take(1)
            .collect::<Vec<_>>()
            .first()
            .map(|&(ref handler, ref bindings)| {
                let mut map = BindingMap::new(Some(&handler.closure.parent_bindings));
                for (key, value) in bindings.iter() {
                    map.local_assign(key, value.to_owned());
                }
                trace!("bindings: {:?}", bindings);
                (handler.closure.instructions.clone(), map)
            });

        if let Some((instructions, map)) = matched_handler {
            trace!("instructions: {:?}", instructions);
            self.reset_instructions(instructions, Some(map));
        } else {
            debug!("Uncaught exception ignored: {:?}", value);
        }
    }

    fn reset_instructions(
        &mut self,
        instructions: Rc<InstructionSequence>,
        binding_map: Option<BindingMap>,
    ) {
        if let Some(map) = binding_map {
            self.frames.push(Frame::new(map));
        }
        self.instructions = instructions.clone();
        self.pc = 0;
        trace!("instructions have been reset!");
    }

    fn next_instruction(vm: &mut Vm) -> Option<Instruction> {
        let instruction = match vm.instructions.get(vm.pc) {
            Some(i) => Some(i.clone()),
            None => None,
        };

        vm.pc += 1;

        instruction
    }

    fn literal_to_value<'b>(literal: &'b Literal, top_bindings: &BindingMap) -> Value {
        match literal {
            &Literal::Number(ref num) => Value::Number(num.to_owned()),
            &Literal::CharString(ref str) => Value::CharString(str.to_string()),
            &Literal::Fn(ref args, ref statements) => {
                // Statements should be compiled ahead of time
                let closure = Closure::new(Rc::new(compile(&statements)), top_bindings);
                Value::Closure(Rc::new(args.clone()), Rc::new(closure))
            }
            _ => panic!("not implemented"),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use test_helpers::*;

    #[test]
    fn run_simple() {
        let mut vm = Vm::new(
            r#"let a = 1
        let b = { "a" => 1 }"#,
        );
        vm.run();
        assert_eq!(
            v_number(1, 1),
            vm.fetch(&"a".to_owned()).unwrap().to_owned()
        );
        let expected_map = v_map(vec![(v_string("a"), v_number(1, 1))]);
        assert_eq!(expected_map, vm.fetch(&"b".to_owned()).unwrap().to_owned())
    }

    #[test]
    fn function_call() {
        let source = r#"let a = ""
            let x = fn() do
              a = 1
            end
            x()"#;

        let mut vm = Vm::new(source);
        vm.run();
        assert_eq!(
            v_number(1, 1),
            vm.fetch(&"a".to_owned()).unwrap().to_owned()
        )
    }

    #[test]
    fn function_call_with_args() {
        let source = r#"let a = ""
            let b = ""
            let x = fn(c, d) do
              a = c
              b = d
            end
            x(1, 2)"#;

        let mut vm = Vm::new(source);
        vm.run();
        assert_eq!(
            v_number(1, 1),
            vm.fetch(&"a".to_owned()).unwrap().to_owned()
        );
        assert_eq!(
            v_number(2, 1),
            vm.frames
                .last()
                .unwrap()
                .bindings
                .fetch(&"b".to_owned())
                .unwrap()
                .to_owned()
        )
    }

    #[test]
    #[should_panic(expected = "expected a closure")]
    fn calling_non_function() {
        let source = r#"let x = ""
            x()"#;
        let mut vm = Vm::new(source);
        vm.run();
    }

    #[test]
    #[should_panic(expected = "wrong number of arguments")]
    fn function_with_wrong_arg_count() {
        let source = "let x = fn(a, b) do
            end
            x(1)";

        let mut vm = Vm::new(source);
        vm.run();
    }

    #[test]
    fn basic_rescue() {
        let source = r#"let a = ""
            rescue(id) do
                a = id
            end
            let x = fn(b) do
                raise(b)
            end
            x(1)"#;

        let mut vm = Vm::new(source);
        vm.run();
        assert_eq!(
            v_number(1, 1),
            vm.fetch(&"a".to_owned()).unwrap().to_owned()
        )
    }

    #[test]
    fn rescue_map() {
        let source = r#"let a = ""
            rescue({"b" => id}) do
                a = id
            end
            let x = fn(a, b) do
                raise({"a" => 1, "b" => b})
            end
            x(2, 1)"#;

        let mut vm = Vm::new(source);
        vm.run();
        assert_eq!(
            v_number(1, 1),
            vm.fetch(&"a".to_owned()).unwrap().to_owned()
        )
    }

    #[test]
    fn maps() {
        let source = r#"let a = { "c" => 1 }
            a["b"] = 2
            let b = a["b"]
            let c = a["c"]
            let d = a + { "e" => 3 }"#;

        let mut vm = Vm::new(source);
        vm.run();

        assert_eq!(
            v_number(2, 1),
            vm.fetch(&"b".to_owned()).unwrap().to_owned()
        );
        assert_eq!(
            v_number(1, 1),
            vm.fetch(&"c".to_owned()).unwrap().to_owned()
        );
        assert_eq!(
            v_map(vec![
                (v_string("c"), v_number(1, 1)),
                (v_string("b"), v_number(2, 1)),
            ]),
            vm.fetch(&"a".to_owned()).unwrap().to_owned()
        );
        assert_eq!(
            v_map(vec![
                (v_string("c"), v_number(1, 1)),
                (v_string("b"), v_number(2, 1)),
                (v_string("e"), v_number(3, 1)),
            ]),
            vm.fetch(&"d".to_owned()).unwrap().to_owned()
        );
    }

    #[test]
    fn fibonacci() {
        let source = r#"let fib = fn(k) do
          rescue({ "m" => m, "k" => 0 }) do
            raise({ "result" => m })
          end
          rescue({ "m" => m, "n" => n, "k" => k }) do
            raise({ "m" => n, "n" => m + n, "k" => k - 1 })
          end
          raise({ "m" => 0, "n" => 1, "k" => k })
        end
        let res = ""
        let setup = fn() do
          rescue({ "result" => r }) do
            res = r
          end
          fib(6)
        end
        setup()"#;

        let mut vm = Vm::new(source);
        vm.run();
        assert_eq!(
            v_number(8, 1),
            vm.fetch(&"res".to_owned()).unwrap().to_owned()
        )
    }

    #[test]
    fn import_file() {
        let mut buffer = File::create("read_test.txt").unwrap();

        buffer.write(b"file content").unwrap();

        let source = r#"let a = import("file")
             let res = ""
             rescue({"file.result" => r}) do
               res = r
             end
             a.read("read_test.txt")"#;

        let mut vm = Vm::new(source);
        vm.run();
        assert_eq!(
            v_string("file content"),
            vm.fetch(&"res".to_owned()).unwrap().to_owned()
        );

        fs::remove_file("read_test.txt").unwrap();
    }
}
<commit_msg>Remove catchall case for unknown instruction<commit_after>use ast::*;
use binding_map::BindingMap;
use closure::Closure;
use compiler::*;
use grammar::*;
use instructions::*;
use native::find_lib;
use native::FileDescriptorMap;
use std::fs;
use std::fs::File;
use std::io::Write;
use value::Value;

use exception_handler::ExceptionHandler;

use std::cell::RefCell;
use std::rc::Rc;

#[derive(Clone, Eq, Debug, PartialEq)]
struct Frame {
    bindings: BindingMap,
    exception_handlers: Vec<ExceptionHandler>,
}

impl Frame {
    pub fn new(bindings: BindingMap) -> Frame {
        Frame {
            bindings: bindings,
            exception_handlers: Vec::new(),
        }
    }
}

#[derive(Clone, Eq, Debug, PartialEq)]
pub struct Vm {
    instructions: Rc<InstructionSequence>,
    pc: usize,
    stack: Vec<Value>,
    frames: Vec<Frame>,
    pub file_descriptors: FileDescriptorMap,
}

impl Vm {
    pub fn new(source: &str) -> Vm {
        let stmts = statements(source);
        let instructions = compile(&stmts.unwrap());
        let map = BindingMap::new(None);
        let frame = Frame::new(map);

        let vm = Vm {
            instructions: Rc::new(instructions),
            pc: 0,
            stack: Vec::new(),
            frames: vec![frame],
            file_descriptors: FileDescriptorMap::new(),
        };
        vm
    }

    pub fn empty() -> Vm {
        let map = BindingMap::new(None);
        let frame = Frame::new(map);
        let vm = Vm {
            instructions: Rc::new(vec![]),
            pc: 0,
            stack: Vec::new(),
            frames: vec![frame],
            file_descriptors: FileDescriptorMap::new(),
        };
        vm
    }

    pub fn run<'b>(&'b mut self) {
        loop {
            let insn_result = Vm::next_instruction(self);
            let instruction;

            instruction = if let Some(i) = insn_result {
                trace!("next instruction: {:?}", i);
                i
            } else {
                trace!("instruction {:?} not found, terminating", insn_result);
                break;
            };

            match instruction {
                Instruction::Clear => self.stack.clear(),
                Instruction::Push(ref value) => {
                    let top_bindings = &mut self.frames.last_mut().unwrap().bindings;
                    self.stack.push(Vm::literal_to_value(value, top_bindings))
                }
                Instruction::Assign(ref binding_name) => {
                    let value = self.stack.pop().unwrap();
                    self.frames
                        .last_mut()
                        .unwrap()
                        .bindings
                        .assign(binding_name, value)
                }
                Instruction::LocalAssign(ref binding_name) => {
                    let value = self.stack.pop().unwrap();
                    self.frames
                        .last_mut()
                        .unwrap()
                        .bindings
                        .local_assign(binding_name, value)
                }
                Instruction::Call(arg_size) => {
                    let closure_info = match self.stack.pop() {
                        Some(Value::Closure(arg_names, closure)) => Ok((arg_names, closure)),
                        Some(x) => Err(format!("expected a closure, got {:?}", x)),
                        None => Err(format!("expected a closure, got None")),
                    };

                    let new_stack_length = { self.stack.len() - arg_size };
                    let mut args = self.stack.split_off(new_stack_length);
                    let (closure_args, closure) = match closure_info {
                        Ok(info) => info,
                        Err(m) => panic!(m),
                    };
                    if arg_size != closure_args.len() {
                        panic!(
                            "wrong number of arguments, expected {:?}, got {:?}",
                            closure_args.len(),
                            arg_size
                        )
                    };
                    let local_bindings = (*closure_args)
                        .clone()
                        .into_iter()
                        .rev()
                        .map(|arg_name| (arg_name, args.pop().unwrap()))
                        .collect();

                    self.reset_instructions(
                        closure.instructions.clone(),
                        Some(closure.init_map(local_bindings)),
                    );
                }
                Instruction::Fetch(ref binding_name) => {
                    let value = self.fetch(binding_name).unwrap();
                    self.stack.push(value);
                }
                Instruction::MakeMap(size) => {
                    let map = (0..size)
                        .into_iter()
                        .map(|_| {
                            let value = self.stack.pop().unwrap();
                            let key = self.stack.pop().unwrap();
                            (key, value)
                        })
                        .collect();
                    self.stack.push(Value::Map(Rc::new(RefCell::new(map))))
                }
                Instruction::Rescue(ref pattern, ref iseq) => {
                    let top_bindings = { &mut self.frames.last_mut().unwrap().bindings.clone() };
                    let closure = Closure::new(iseq.clone(), top_bindings);
                    self.frames
                        .last_mut()
                        .unwrap()
                        .exception_handlers
                        .push(ExceptionHandler::new(pattern.clone(), closure))
                }
                Instruction::Raise => {
                    let raised_value = self.stack.pop().unwrap();
                    self.raise(raised_value);
                }
                Instruction::BinOp(op) => {
                    let right = self.stack.pop().unwrap();
                    let left = self.stack.pop().unwrap();

                    let binop_result = match op {
                        Op::Add => left.add(right),
                        Op::Sub => left.sub(right),
                        Op::Mul => left.mul(right),
                        Op::Div => left.div(right),
                    };

                    if let Ok(result) = binop_result {
                        self.stack.push(result);
                    } else {
                        // TODO: Raise
                    }
                }
                Instruction::IndexAccess => {
                    let property = self.stack.pop().unwrap();
                    let target = self.stack.pop().unwrap();

                    match target {
                        Value::Map(ref map) => {
                            if let Some(value) = map.borrow().get(&property) {
                                self.stack.push((*value).clone());
                            } else {
                                panic!("no value for {:?}", target); // TODO: Raise
                            }
                        }
                        v => panic!("can't use index access for {:?}", v), // TODO: Raise
                    };
                }
                Instruction::IndexAssign => {
                    let value = self.stack.pop().unwrap();
                    let property = self.stack.pop().unwrap();
                    let mut target = self.stack.pop().unwrap();

                    match target {
                        Value::Map(ref mut map) => {
                            map.borrow_mut().insert(property, value);
                        }
                        v => panic!("can't use index access for {:?}", v), // TODO: Raise
                    };
                }
                Instruction::Import => {
                    let name = self.stack.pop().unwrap();
                    if let Value::CharString(ref str) = name {
                        if let Some(lib) = find_lib(str) {
                            self.stack.push(lib.clone());
                        }
                    } else {
                        panic!("import value must be a string"); // TODO: Raise
                    }
                }
                Instruction::Native(native_fn) => {
                    trace!("Starting native code");
                    let instructions = native_fn.call(self);
                    trace!("Finished native code");
                    self.reset_instructions(Rc::new(instructions), None)
                }
            };
        }
    }

    pub fn push(&mut self, value: Value) {
        self.stack.push(value);
    }

    pub fn pop(&mut self) -> Option<Value> {
        self.stack.pop()
    }

    pub fn fetch(&mut self, name: &String) -> Option<Value> {
        self.bindings().fetch(name)
    }

    pub fn local_assign(&mut self, name: &String, value: Value) {
        self.bindings().local_assign(name, value);
    }

    fn bindings(&mut self) -> &mut BindingMap {
        &mut self.frames.last_mut().unwrap().bindings
    }

    fn raise(&mut self, value: Value) {
        let matched_handler = self
            .frames
            .iter()
            .rev()
            .filter_map(|frame| {
                let handlers = frame
                    .exception_handlers
                    .iter()
                    .filter_map(|handler| match handler.matches(value.clone()) {
                        Some(bindings) => Some((handler, bindings)),
                        None => None,
                    })
                    .collect::<Vec<_>>();

                if handlers.is_empty() {
                    return None;
                }

                trace!("found handlers: {:?}", handlers.len());
                Some(handlers.first().unwrap().clone())
            })
            .take(1)
            .collect::<Vec<_>>()
            .first()
            .map(|&(ref handler, ref bindings)| {
                let mut map = BindingMap::new(Some(&handler.closure.parent_bindings));
                for (key, value) in bindings.iter() {
                    map.local_assign(key, value.to_owned());
                }
                trace!("bindings: {:?}", bindings);
                (handler.closure.instructions.clone(), map)
            });

        if let Some((instructions, map)) = matched_handler {
            trace!("instructions: {:?}", instructions);
            self.reset_instructions(instructions, Some(map));
        } else {
            debug!("Uncaught exception ignored: {:?}", value);
        }
    }

    fn reset_instructions(
        &mut self,
        instructions: Rc<InstructionSequence>,
        binding_map: Option<BindingMap>,
    ) {
        if let Some(map) = binding_map {
            self.frames.push(Frame::new(map));
        }
        self.instructions = instructions.clone();
        self.pc = 0;
        trace!("instructions have been reset!");
    }

    fn next_instruction(vm: &mut Vm) -> Option<Instruction> {
        let instruction = match vm.instructions.get(vm.pc) {
            Some(i) => Some(i.clone()),
            None => None,
        };

        vm.pc += 1;

        instruction
    }

    fn literal_to_value<'b>(literal: &'b Literal, top_bindings: &BindingMap) -> Value {
        match literal {
            &Literal::Number(ref num) => Value::Number(num.to_owned()),
            &Literal::CharString(ref str) => Value::CharString(str.to_string()),
            &Literal::Fn(ref args, ref statements) => {
                // Statements should be compiled ahead of time
                let closure = Closure::new(Rc::new(compile(&statements)), top_bindings);
                Value::Closure(Rc::new(args.clone()), Rc::new(closure))
            }
            _ => panic!("not implemented"),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use test_helpers::*;

    #[test]
    fn run_simple() {
        let mut vm = Vm::new(
            r#"let a = 1
        let b = { "a" => 1 }"#,
        );
        vm.run();
        assert_eq!(
            v_number(1, 1),
            vm.fetch(&"a".to_owned()).unwrap().to_owned()
        );
        let expected_map = v_map(vec![(v_string("a"), v_number(1, 1))]);
        assert_eq!(expected_map, vm.fetch(&"b".to_owned()).unwrap().to_owned())
    }

    #[test]
    fn function_call() {
        let source = r#"let a = ""
            let x = fn() do
              a = 1
            end
            x()"#;

        let mut vm = Vm::new(source);
        vm.run();
        assert_eq!(
            v_number(1, 1),
            vm.fetch(&"a".to_owned()).unwrap().to_owned()
        )
    }

    #[test]
    fn function_call_with_args() {
        let source = r#"let a = ""
            let b = ""
            let x = fn(c, d) do
              a = c
              b = d
            end
            x(1, 2)"#;

        let mut vm = Vm::new(source);
        vm.run();
        assert_eq!(
            v_number(1, 1),
            vm.fetch(&"a".to_owned()).unwrap().to_owned()
        );
        assert_eq!(
            v_number(2, 1),
            vm.frames
                .last()
                .unwrap()
                .bindings
                .fetch(&"b".to_owned())
                .unwrap()
                .to_owned()
        )
    }

    #[test]
    #[should_panic(expected = "expected a closure")]
    fn calling_non_function() {
        let source = r#"let x = ""
            x()"#;
        let mut vm = Vm::new(source);
        vm.run();
    }

    #[test]
    #[should_panic(expected = "wrong number of arguments")]
    fn function_with_wrong_arg_count() {
        let source = "let x = fn(a, b) do
            end
            x(1)";

        let mut vm = Vm::new(source);
        vm.run();
    }

    #[test]
    fn basic_rescue() {
        let source = r#"let a = ""
            rescue(id) do
                a = id
            end
            let x = fn(b) do
                raise(b)
            end
            x(1)"#;

        let mut vm = Vm::new(source);
        vm.run();
        assert_eq!(
            v_number(1, 1),
            vm.fetch(&"a".to_owned()).unwrap().to_owned()
        )
    }

    #[test]
    fn rescue_map() {
        let source = r#"let a = ""
            rescue({"b" => id}) do
                a = id
            end
            let x = fn(a, b) do
                raise({"a" => 1, "b" => b})
            end
            x(2, 1)"#;

        let mut vm = Vm::new(source);
        vm.run();
        assert_eq!(
            v_number(1, 1),
            vm.fetch(&"a".to_owned()).unwrap().to_owned()
        )
    }

    #[test]
    fn maps() {
        let source = r#"let a = { "c" => 1 }
            a["b"] = 2
            let b = a["b"]
            let c = a["c"]
            let d = a + { "e" => 3 }"#;

        let mut vm = Vm::new(source);
        vm.run();

        assert_eq!(
            v_number(2, 1),
            vm.fetch(&"b".to_owned()).unwrap().to_owned()
        );
        assert_eq!(
            v_number(1, 1),
            vm.fetch(&"c".to_owned()).unwrap().to_owned()
        );
        assert_eq!(
            v_map(vec![
                (v_string("c"), v_number(1, 1)),
                (v_string("b"), v_number(2, 1)),
            ]),
            vm.fetch(&"a".to_owned()).unwrap().to_owned()
        );
        assert_eq!(
            v_map(vec![
                (v_string("c"), v_number(1, 1)),
                (v_string("b"), v_number(2, 1)),
                (v_string("e"), v_number(3, 1)),
            ]),
            vm.fetch(&"d".to_owned()).unwrap().to_owned()
        );
    }

    #[test]
    fn fibonacci() {
        let source = r#"let fib = fn(k) do
          rescue({ "m" => m, "k" => 0 }) do
            raise({ "result" => m })
          end
          rescue({ "m" => m, "n" => n, "k" => k }) do
            raise({ "m" => n, "n" => m + n, "k" => k - 1 })
          end
          raise({ "m" => 0, "n" => 1, "k" => k })
        end
        let res = ""
        let setup = fn() do
          rescue({ "result" => r }) do
            res = r
          end
          fib(6)
        end
        setup()"#;

        let mut vm = Vm::new(source);
        vm.run();
        assert_eq!(
            v_number(8, 1),
            vm.fetch(&"res".to_owned()).unwrap().to_owned()
        )
    }

    #[test]
    fn import_file() {
        let mut buffer = File::create("read_test.txt").unwrap();

        buffer.write(b"file content").unwrap();

        let source = r#"let a = import("file")
             let res = ""
             rescue({"file.result" => r}) do
               res = r
             end
             a.read("read_test.txt")"#;

        let mut vm = Vm::new(source);
        vm.run();
        assert_eq!(
            v_string("file content"),
            vm.fetch(&"res".to_owned()).unwrap().to_owned()
        );

        fs::remove_file("read_test.txt").unwrap();
    }
}
<|endoftext|>