castwide / solargraph

A Ruby language server.

Home Page:https://solargraph.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Static type checker

castwide opened this issue · comments

Gem version 0.33.0 introduces the first version of the static type checker. You can use it from the command line or add it to your editor's diagnostics.

Command Line

Run it from the command line:

cd /path/to/project
solargraph typecheck

The output will list all methods that have missing or invalid @return and @param tags.

To check a specific file only, use solargraph typecheck ./file.rb. (The type checker will still map the rest of the workspace for better accuracy.)

The optional --strict argument uses static code analysis to ensure that methods and params are tagged with the correct types. This option is highly experimental. You can expect a lot of false positives and potential bugs.

Language Server Diagnostics

You can get typecheck diagnostics from the language server with the typecheck reporter. Enable it by adding it to your workspace's .solargraph.yml:

reporters:
- typecheck

For strict type checking (warning: expect bugs):

reporters:
- typecheck:strict

This is very much a work in progress. Any feedback is appreciated.

The output will list all methods that have missing or invalid @return and @param tags.

This checks if there is a type defined for params and return tags in the doc-comments? But it doesn't Analyse code paths to see if incorrect type might be passed to the methods?

The normal type check only checks @return and @param tags, but strict type checks analyze code paths.

class Example
  # Type check is validated here; both @param and @return have resolvable types
  # @param num [Integer]
  # @return [self]
  def use_integer(num)
    self
  end
end

# This method call will report an error in strict type checks (Integer expected)
Example.new.use_integer('string')

Gem 0.34.0 includes some updates to the type checker, including support for validating overload tags and preliminary support for validating duck types in strict mode.

These are the proposed levels for type checking:

Normal

  • @return and @param tags are optional
  • Resolve namespaces in all type tags
  • Ignore all undefined types

Typed

  • @return and @param tags are optional
  • Resolve namespaces in all type tags
  • Validate existing @return tags against inferred types
  • Validate existing @type tags against inferred types
  • Validate existing @param tags against arguments
  • Loose @return tag matches
  • Ignore all undefined types

Strict

  • @param tags are optional
  • All methods must have either a @return tag or an inferred type
  • Resolve namespaces in all type tags
  • Validate existing @return tags against inferred types
  • Validate existing @param tags against arguments
  • Validate existing @type tags against inferred types
  • Strict @return tag matches
  • Validate method calls
  • Ignore undefined types from external sources

Strong

  • @return and @param tags are required
  • Resolve namespaces in all type tags
  • Validate @return tags against inferred types
  • Validate @param tags against arguments
  • Validate existing @type tags against inferred types
  • Strict @return tag matches
  • Validate method calls
  • Ignore undefined types from external sources

Other notes:

  • Handling undefined types should be similar to handling Any in TypeScript. Every level is capable of allowing undefined types with varying degrees of strictness.
  • Ignoring undefined types from external sources ensures that type checking does not report errors from untyped vendor code, such as gem dependencies.
  • A possible level higher than "strong" could validate types from external sources as well.
  • Array and Hash parameters go largely ignored in validation. This means that Array, Array<String>, and Array<Integer> would all be considered the same type for validation purposes. Validation of type parameters is a possibility in the future.

A possible level higher than "strong" could validate types from external sources as well.

Call it "unreasonable", haha.

Glad to see continued progress on this!

A possible level higher than "strong" could validate types from external sources as well.

That could be useful for API stubs. We generate API stubs - a lot of it have @param and @return types, but the code base predated YARD so we're still backfilling. But the stubs also have no content in the methods, so little to infer from the code.

The v0.39 branch includes the current version of the type checker.

To run it from the command line, use solargraph typecheck. The --level option sets the check level.

To test diagnostics in the language server, add typecheck to the reporters section in .solargraph.yml:

reporters:
- typecheck

You can also add an argument for the check level, e.g., typecheck:typed or typecheck:strict.

@castwide

        # @type [Array]
        resources = Dir.chdir(path) do # error: Declared type Array does not match inferred type Object for variable resources
          Dir['**/*'].reject(&::File.method(:directory?))
        end

should support cast annotation from superclass to subclass.
and the Dir.chdir block return the value block returns, the infer can work better

@castwide how to silence the infer error? check level is strict

module A
  # @return [Hash{String => Hash, Array}]
  def index # A#index return type could not be inferred
    @index ||= begin
                 recur = lambda do |i, resources|
                   table = {}
                   table[i] = recur[i + 1, resources]
                   table
                 end
                 recur[0, @resources]
               end
  end
end

@SolaWing There's no way to silence specific errors. That might be a good feature to add. For now the only workaround is to reduce the check level. Typed doesn't report errors for uninferred return types.

Also, regarding your Dir.chdir example:

  1. The type checker should definitely recognize that Array is a subclass of Object. I'll look into why that example doesn't work.
  2. I'm working on a mechanism to include the results of yielded blocks in type inference.

The type checker now handles inheritance in either direction when checking tagged vs. inferred types. Array -> Object and Object -> Array are both valid; Array -> String is not.

Released in v0.39.0.

More information: https://solargraph.org/guides/type-checking

since the checker supported upcast and downcast, add a Object type to declare type, can suppress the wrong infer or wrong extern annotation type.

just for someone what strict check but struggle with typechecker.