Skip to content
This repository has been archived by the owner on Apr 23, 2024. It is now read-only.

Commit

Permalink
cmd: added initial support of PHP 8 (#19)
Browse files Browse the repository at this point in the history
At the moment we are correctly parsing all PHP 8 and 8.1
except intersection types.

A nullsafe method call is treated like a normal method
call and creates a link.

PHP 8 is now by default. In order to parse the project as
PHP 7, added the `--php7` flag.
  • Loading branch information
i582 authored Aug 20, 2021
1 parent 5680370 commit 88744c9
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 14 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
33 changes: 28 additions & 5 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -108,30 +108,53 @@ 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
ctx.ParsedFlags.ReportsCritical = cmd.AllNonNoticeChecks

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)
Expand Down
12 changes: 12 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
```
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
10 changes: 4 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
Expand All @@ -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=
Expand All @@ -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=
Expand Down
2 changes: 1 addition & 1 deletion internal/linttest/linttest.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions internal/walkers/block_checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
23 changes: 23 additions & 0 deletions internal/walkers/root_checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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 {
Expand Down
42 changes: 42 additions & 0 deletions tests/edges/php8_test.go
Original file line number Diff line number Diff line change
@@ -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(`<?php
class Foo {
/**
* @color red
*/
public function f() {}
}
/**
* @color green
*/
function f($cond) {
$a = new Foo;
if ($cond) {
$a = null;
}
$a?->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()
}

0 comments on commit 88744c9

Please sign in to comment.