diff --git a/CHANGELOG.md b/CHANGELOG.md index 434bcff..7dbc768 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,26 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## Unreleased + +Due to the addition of PHP 8 support, now by default all projects will be parsed as **PHP 8**. If your project uses the `real` cast, the `is_real` function, comments like `#[...` or `match` and `enum` keywords, then use the `--php7` flag to make the analyzer parse the project like PHP 7.4. + +Previously, in order to take into account the `vendor` folder for better type inference, it was necessary to use the `--index-only-files` flag, now the `vendor` folder is added to this flag by default if it exists, so you no longer need to explicitly pass it. + +### Added + +- [#19](https://github.com/VKCOM/nocolor/pull/19): Added initial support of PHP 8 +- [#19](https://github.com/VKCOM/nocolor/pull/19): Added flag `--php7` for analyze as PHP 7 + +### Changed + +- [#19](https://github.com/VKCOM/nocolor/pull/19): Moved to new version of NoVerify: + - PHP 8 and 8.1 initial support + - Improvements in type inference (`instanceof`, `callable` in PHPDoc, `array{}`) + - Help now has grouping for flags + - `vendor` folder is now added by default if it exists + + ## `1.0.4` 2021-01-07 > If you used version **1.0.3** and below, then remove the current cache with the `cache-clear` command. diff --git a/cmd/check.go b/cmd/check.go index c51c6cf..f1bd8fc 100644 --- a/cmd/check.go +++ b/cmd/check.go @@ -40,7 +40,7 @@ func Check(ctx *cmd.AppContext, globalContext *walkers.GlobalContext) (status in } // The main function for analyzing in NoVerify, - // in it we collect all the functions of the project. + // in it, we collect all the functions of the project. _, err = cmd.Check(ctx) if len(LinterReports) != 0 { HandleShowLinterReports(ctx, LinterReports) diff --git a/cmd/main.go b/cmd/main.go index 61210aa..60f3c0b 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -55,7 +55,7 @@ func DefaultCacheDir() string { // Main is the function that launches the program. func Main() { - config := linter.NewConfig() + config := linter.NewConfig("8.1") context := walkers.NewGlobalContext(nil) status, err := cmd.Run(&cmd.MainConfig{ @@ -108,19 +108,40 @@ func Main() { Description: "Folders or files for analysis", }, }, - RegisterFlags: func(ctx *cmd.AppContext) *flag.FlagSet { + RegisterFlags: func(ctx *cmd.AppContext) (*flag.FlagSet, *cmd.FlagsGroups) { flags := &extraCheckFlags{} fs := flag.NewFlagSet("check", flag.ContinueOnError) + groups := cmd.NewFlagsGroups() + + groups.AddGroup("Color") + groups.AddGroup("Language") + groups.AddGroup("Files") + groups.AddGroup("Additional") // We don't need all the flags from NoVerify, so we only register some of them. fs.IntVar(&ctx.ParsedFlags.MaxFileSize, "max-sum-filesize", 10*1024*1024, "Max total file size to be parsed concurrently in bytes (limits max memory consumption)") fs.IntVar(&ctx.ParsedFlags.MaxConcurrency, "cores", runtime.NumCPU(), "Max number of cores to use") - fs.BoolVar(&ctx.ParsedFlags.DisableCache, "disable-cache", false, "If set, cache is not used and cache-dir is ignored") fs.StringVar(&ctx.ParsedFlags.StubsDir, "stubs-dir", "", "Directory with custom phpstorm-stubs") fs.StringVar(&ctx.ParsedFlags.CacheDir, "cache-dir", DefaultCacheDir(), "Directory for linter cache (greatly improves indexing speed)") + fs.BoolVar(&ctx.ParsedFlags.DisableCache, "disable-cache", false, "If set, cache is not used and cache-dir is ignored") + + groups.Add("Additional", "cores") + groups.Add("Additional", "cache-dir") + groups.Add("Additional", "disable-cache") + groups.Add("Additional", "max-sum-filesize") + groups.Add("Additional", "stubs-dir") + fs.StringVar(&ctx.ParsedFlags.IndexOnlyFiles, "index-only-files", "", "Comma-separated list of paths to files, which should be indexed, but not analyzed") fs.StringVar(&ctx.ParsedFlags.PhpExtensionsArg, "php-exts", "php,inc,php5,phtml", "List of PHP file extensions to be analyzed") + fs.StringVar(&flags.Output, "output", "", "Path to the file where the errors will be written in JSON format") + + groups.Add("Files", "index-only-files") + groups.Add("Files", "php-exts") + groups.Add("Files", "output") + + fs.BoolVar(&ctx.ParsedFlags.PHP7, "php7", false, "Analyze as PHP 7") + groups.Add("Language", "php7") // Some values need to be set manually. ctx.ParsedFlags.AllowAll = true @@ -128,10 +149,12 @@ func Main() { fs.StringVar(&flags.PaletteSrc, "palette", "palette.yaml", "File with color palette") fs.StringVar(&flags.ColorTag, "tag", "color", "The tag to be used to set the color in PHPDoc") - fs.StringVar(&flags.Output, "output", "", "Path to the file where the errors will be written in JSON format") + + groups.Add("Color", "palette") + groups.Add("Color", "tag") ctx.CustomFlags = flags - return fs + return fs, groups }, Action: func(ctx *cmd.AppContext) (int, error) { return Check(ctx, context) diff --git a/docs/configuration.md b/docs/configuration.md index 8e3f5ee..c1757b4 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -2,6 +2,15 @@ This page is dedicated to some technical details. +## How to parse as PHP 7 + +It looks like this: + +```bash +nocolor check --php7 ./src +``` + +By default, all code is parsed as PHP 8, however, some projects use names that have become reserved in PHP 8, so they need to be parsed as PHP 7. ## How to exclude some folders from checking @@ -16,7 +25,10 @@ The `--index-only-files` option sets paths that won't be analyzed, they will be ## How to include the `vendor` folder +> Since version 1.1, the `vendor` folder is added by default if it exists. + Using the same option: + ```bash nocolor check --index-only-files='./vendor' ./src ``` diff --git a/go.mod b/go.mod index 9b1c698..c7af5db 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/vkcom/nocolor go 1.16 require ( - github.com/VKCOM/noverify v0.3.1-0.20210630212848-6a7e948f7581 + github.com/VKCOM/noverify v0.4.1-0.20210820112310-17cd2560f7a0 github.com/i582/cfmt v1.3.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 41aafb7..fd733a7 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ -github.com/VKCOM/noverify v0.3.1-0.20210630212848-6a7e948f7581 h1:MQ+VZYed8J0Ocd0tgqFNsjaPI8S0ueObTHX3wrLExOA= -github.com/VKCOM/noverify v0.3.1-0.20210630212848-6a7e948f7581/go.mod h1:g0IgAUfjZ9Z3v+nKj+jRyBnBiWHE3O8f7ZLrRlVHk+k= +github.com/VKCOM/noverify v0.4.1-0.20210820112310-17cd2560f7a0 h1:Wn6Rf+DdoOPRpODjVSfQqMTvQM7DAkRkJXvepB6c0GY= +github.com/VKCOM/noverify v0.4.1-0.20210820112310-17cd2560f7a0/go.mod h1:4YqmJFxSi6Hw7kBiUZ7GzeQgqHLyxEopsSVhIf9/htA= +github.com/VKCOM/php-parser v0.8.0-rc.2.0.20210802093708-d85f5a481602 h1:FYasGDmh13Lr5zwqIkxoYZsr/YlEIMTIpqzIu/syeYY= +github.com/VKCOM/php-parser v0.8.0-rc.2.0.20210802093708-d85f5a481602/go.mod h1:wLtaD4M5K8bJPwjkl4BVone8dbMiG1OApbMKdjubCEw= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= @@ -11,8 +13,6 @@ github.com/gookit/color v1.3.2 h1:WO8+16ZZtx+HlOb6cueziUAF8VtALZKRr/jOvuDk0X0= github.com/gookit/color v1.3.2/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ= github.com/i582/cfmt v1.3.0 h1:UQWMxXt5+iAAtWLD8XUjjGcHCvLM2tRjarvUQs6Ij5o= github.com/i582/cfmt v1.3.0/go.mod h1:tpHWAxhE4Y7yy7sliaNe0pnnEs1SZe67KLljyOlEYI8= -github.com/karrick/godirwalk v1.15.6 h1:Yf2mmR8TJy+8Fa0SuQVto5SYap6IF7lNVX4Jdl8G1qA= -github.com/karrick/godirwalk v1.15.6/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -26,8 +26,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/yookoala/realpath v1.0.0/go.mod h1:gJJMA9wuX7AcqLy1+ffPatSCySA1FQ2S8Ya9AIoYBpE= -github.com/z7zmey/php-parser v0.8.0-rc.1.0.20210213215434-367eff9de651 h1:gDfdOvHmWWFqE/LltouUfWGq7Z3PvR4OoOr3ZRwa9J4= -github.com/z7zmey/php-parser v0.8.0-rc.1.0.20210213215434-367eff9de651/go.mod h1:aae81kYAwd9gCu7qaA7neWjT77MIRtjPqpkXFzSkKIQ= go.lsp.dev/uri v0.3.0/go.mod h1:P5sbO1IQR+qySTWOCnhnK7phBx+W3zbLqSMDJNTw88I= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/linttest/linttest.go b/internal/linttest/linttest.go index 1c2a348..f0233e6 100644 --- a/internal/linttest/linttest.go +++ b/internal/linttest/linttest.go @@ -34,7 +34,7 @@ type Suite struct { // NewSuite returns a new linter test suite for t. func NewSuite(t testing.TB) *Suite { - conf := linter.NewConfig() + conf := linter.NewConfig("8.1") return &Suite{ t: t, config: conf, diff --git a/internal/walkers/block_checker.go b/internal/walkers/block_checker.go index b2e3526..15a6cc3 100644 --- a/internal/walkers/block_checker.go +++ b/internal/walkers/block_checker.go @@ -46,6 +46,8 @@ func (b *BlockChecker) AfterEnterNode(n ir.Node) { b.root.handleStaticCall(n, b.ctx.Scope()) case *ir.MethodCallExpr: b.root.handleMethodCall(n, b.ctx.Scope(), b) + case *ir.NullsafeMethodCallExpr: + b.root.handleNullsafeMethodCall(n, b.ctx.Scope(), b) case *ir.ImportExpr: b.root.handleImportExpr(n) case *ir.CloneExpr: diff --git a/internal/walkers/root_checker.go b/internal/walkers/root_checker.go index b64c973..8e1d031 100644 --- a/internal/walkers/root_checker.go +++ b/internal/walkers/root_checker.go @@ -85,6 +85,8 @@ func (r *RootChecker) AfterEnterNode(n ir.Node) { r.handleStaticCall(n, nil) case *ir.MethodCallExpr: r.handleMethodCall(n, nil, r) + case *ir.NullsafeMethodCallExpr: + r.handleNullsafeMethodCall(n, nil, r) case *ir.PropertyFetchExpr: r.handlePropertyFetch(n, nil, irutil.NodePath{}) case *ir.ImportExpr: @@ -373,6 +375,27 @@ func (r *RootChecker) handleMethodCall(n *ir.MethodCallExpr, blockScope *meta.Sc } } +func (r *RootChecker) handleNullsafeMethodCall(n *ir.NullsafeMethodCallExpr, blockScope *meta.Scope, v ir.Visitor) { + method, ok := n.Method.(*ir.Identifier) + if !ok { + return + } + methodName := method.Value + + scope := blockScope + if scope == nil { + scope = r.ctx.Scope() + } + + classType := solver.ExprType(scope, r.state, n.Variable) + + r.handleMethod(methodName, classType, false) + + for _, nn := range n.Args { + nn.Walk(v) + } +} + func (r *RootChecker) handleNew(n *ir.NewExpr, blockScope *meta.Scope) { className, ok := solver.GetClassName(r.state, n.Class) if !ok { diff --git a/tests/edges/php8_test.go b/tests/edges/php8_test.go new file mode 100644 index 0000000..1d0dbed --- /dev/null +++ b/tests/edges/php8_test.go @@ -0,0 +1,42 @@ +package tests + +import ( + "testing" + + "github.com/vkcom/nocolor/internal/linttest" +) + +func TestPHP8NullsafeMethodCall(t *testing.T) { + suite := linttest.NewSuite(t) + + suite.Palette = defaultPalette + suite.AddFile(`f(); +} +`) + + suite.Expect = []string{ + ` +green red => calling red from green is prohibited + This color rule is broken, call chain: +f@green -> Foo::f@red +`, + } + + suite.RunAndMatch() +}