diff --git a/.gitignore b/.gitignore index fdd1235b0..bb3b410fc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ src/cmd/stubs/phpstorm-stubs/ y.output .idea vendor +dev +src/tests/golden/testdata/quickfix/*.fix diff --git a/example/custom/custom_test.go b/example/custom/custom_test.go index 082a15b3a..3b2e5e41b 100644 --- a/example/custom/custom_test.go +++ b/example/custom/custom_test.go @@ -11,6 +11,7 @@ func TestAssignmentAsExpression(t *testing.T) { addCheckers(test.Config()) test.AddFile(`')`, }, + { + Name: "notStrictTypes", + Default: true, + Quickfix: true, + Comment: "Report strict_types value is not 1 in declare section.", + Before: `declare(strict_types = 0);`, + After: `declare(strict_types = 1);`, + }, + + { + Name: "noDeclareSection", + Default: true, + Quickfix: true, + Comment: "Report declare(strict_types=1) has not been set.", + Before: ` `, + After: `declare(strict_types = 1);`, + }, + { Name: "emptyStmt", Default: true, @@ -1185,8 +1203,8 @@ func DiffReports(gitRepo string, diffArgs []string, changesList []git.Change, ch } } - old := reportListToMap(oldList) - new := reportListToMap(newList) + oldReportMap := reportListToMap(oldList) + newReportMap := reportListToMap(newList) changes := gitChangesToMap(changesList) var mu sync.Mutex @@ -1196,7 +1214,7 @@ func DiffReports(gitRepo string, diffArgs []string, changesList []git.Change, ch limitCh := make(chan struct{}, maxConcurrency) - for filename, list := range new { + for filename, list := range newReportMap { wg.Add(1) go func(filename string, list []*Report) { limitCh <- struct{}{} @@ -1212,7 +1230,7 @@ func DiffReports(gitRepo string, diffArgs []string, changesList []git.Change, ch oldName = filename // full diff mode } - reports, err := diffReportsList(gitRepo, ignoreCommits, diffArgs, filename, c, old[oldName], list) + reports, err := diffReportsList(gitRepo, ignoreCommits, diffArgs, filename, c, oldReportMap[oldName], list) if err != nil { mu.Lock() resErr = err @@ -1266,8 +1284,8 @@ func diffReportsList(gitRepo string, ignoreCommits map[string]struct{}, diffArgs } } - old, oldMaxLine := reportListToPerLineMap(oldList) - new, newMaxLine := reportListToPerLineMap(newList) + oldPerLineMap, oldMaxLine := reportListToPerLineMap(oldList) + newPerLineMap, newMaxLine := reportListToPerLineMap(newList) var maxLine = oldMaxLine if newMaxLine > maxLine { @@ -1284,17 +1302,17 @@ func diffReportsList(gitRepo string, ignoreCommits map[string]struct{}, diffArgs // just deletion if ok && ch.new.HaveRange && ch.new.Range == 0 { oldLine = ch.old.To - newLine-- // cancel the increment of newLine, because code was deleted, no new lines added + newLine-- // cancel the increment of newLine, because code was deleted, no newPerLineMap lines added continue } - res = maybeAppendReports(res, new, old, newLine, oldLine, blame, ignoreCommits) + res = maybeAppendReports(res, newPerLineMap, oldPerLineMap, newLine, oldLine, blame, ignoreCommits) if ok { oldLine = 0 // all changes and additions must be checked for j := newLine + 1; j <= ch.new.To; j++ { newLine = j - res = maybeAppendReports(res, new, old, newLine, oldLine, blame, ignoreCommits) + res = maybeAppendReports(res, newPerLineMap, oldPerLineMap, newLine, oldLine, blame, ignoreCommits) } oldLine = ch.old.To } diff --git a/src/linter/root.go b/src/linter/root.go index 3c23699ac..414e23c3f 100644 --- a/src/linter/root.go +++ b/src/linter/root.go @@ -69,10 +69,12 @@ type rootWalker struct { // name matches the pattern and @linter disable was encountered // strictTypes is true if file contains `declare(strict_types=1)`. - strictTypes bool - strictMixed bool + strictTypes bool + strictMixed bool + declareSection bool - reports []*Report + reports []*Report + quickfix *QuickFixGenerator config *Config @@ -141,7 +143,12 @@ func (d *rootWalker) EnterNode(n ir.Node) (res bool) { } if c.ConstantName.Value == "strict_types" { v, ok := c.Expr.(*ir.Lnumber) - if ok && v.Value == "1" { + if !ok { + continue + } + + d.declareSection = true + if v.Value == "1" { d.strictTypes = true } } diff --git a/src/linter/worker.go b/src/linter/worker.go index 6cfa8c5a0..8e7beb7cd 100644 --- a/src/linter/worker.go +++ b/src/linter/worker.go @@ -315,6 +315,11 @@ func (w *Worker) analyzeFile(file *workspace.File, rootNode *ir.Root) (*rootWalk } walker.afterLeaveFile() + if !walker.declareSection { + walker.Report(rootNode, LevelWarning, "noDeclareSection", "Missed declare(strict_types=1) directive") + walker.addQuickFix("notStrictTypes", walker.quickfix.CreateDeclareStrictTypes(rootNode)) + } + if len(walker.ctx.fixes) != 0 { needApplyFixes := !file.AutoGenerated() || w.config.CheckAutoGenerated diff --git a/src/tests/checkers/anon_class_test.go b/src/tests/checkers/anon_class_test.go index 654a9d9fb..8f5951a69 100644 --- a/src/tests/checkers/anon_class_test.go +++ b/src/tests/checkers/anon_class_test.go @@ -8,6 +8,7 @@ import ( func TestSimpleAnonClass(t *testing.T) { linttest.SimpleNegativeTest(t, `func2()->func3(); func TestAnonClassWithConstructor(t *testing.T) { linttest.SimpleNegativeTest(t, ` $v) { $_ = [$k, $v]; @@ -178,6 +188,7 @@ $_ = [$x]; // Bad func TestForeachSimplify(t *testing.T) { test := linttest.NewSuite(t) test.AddFile(` $y]; } @@ -593,6 +613,7 @@ foreach ([[1, 2, 3, 4]] as list($x, $y,,$z)) { func TestArgsCount(t *testing.T) { test := linttest.NewSuite(t) test.AddFile(` 1, "\xa" => 2]; `) test.Expect = []string{`Duplicate array key "\n"`} @@ -1326,6 +1386,7 @@ $_ = ["\n" => 1, "\xa" => 2]; func TestDuplicateArrayKeyGood(t *testing.T) { linttest.SimpleNegativeTest(t, ` 1, "'" => 1, @@ -1336,6 +1397,7 @@ $valid_quotes = [ func TestDuplicateArrayKey(t *testing.T) { test := linttest.NewSuite(t) test.AddFile(` 'something', @@ -1351,6 +1413,7 @@ function test() { func TestDuplicateArrayKeyWithBoolConstants(t *testing.T) { test := linttest.NewSuite(t) test.AddFile(` 1, true => 2]; func TestDuplicateArrayKeyWithConstants(t *testing.T) { test := linttest.NewSuite(t) test.AddFile(`loadHTML('field = $x; } @@ -44,6 +46,7 @@ echo $v->field; func TestIndexingOrderTraits(t *testing.T) { test := linttest.NewSuite(t) test.AddNamedFile("/foo/A.php", `field = $x; } @@ -76,11 +80,13 @@ echo $v->field; func TestIndexingOrderFuncs(t *testing.T) { test := linttest.NewSuite(t) test.AddNamedFile("/foo/A.php", `acceptThis($foo); func TestMagicGetChaining(t *testing.T) { linttest.SimpleNegativeTest(t, `foo->bar->method(); func TestNonPublicMagicMethods(t *testing.T) { test := linttest.NewSuite(t) test.AddFile(`create() as $item) { func TestDerivedLateStaticBinding(t *testing.T) { linttest.SimpleNegativeTest(t, `onlyInDerived(); func TestStaticResolutionInsideSameClass(t *testing.T) { test := linttest.NewSuite(t) test.AddFile(`name(); @@ -1002,6 +1030,7 @@ function fn4($f4) { func TestInstanceOfElseif1(t *testing.T) { linttest.SimpleNegativeTest(t, `[0-9])~', $s); preg_match('~(?[0-9])~', $s); @@ -27,6 +28,7 @@ func TestRESimplifyMixed(t *testing.T) { test := linttest.NewSuite(t) test.LoadStubs = []string{`stubs/phpstorm-stubs/pcre/pcre.php`} test.AddFile(` x function ungroup($s) { preg_match('/(?:x)/', $s); @@ -146,6 +149,7 @@ func TestRESimplifyChangeDelim(t *testing.T) { test := linttest.NewSuite(t) test.LoadStubs = []string{`stubs/phpstorm-stubs/pcre/pcre.php`} test.AddFile(`good6['y']->value; func TestShapeReturn(t *testing.T) { linttest.SimpleNegativeTest(t, `next->next; func TestTuple(t *testing.T) { linttest.SimpleNegativeTest(t, `', 'a', 'A']); } @@ -40,6 +42,7 @@ func TestStripTagsString1(t *testing.T) { test := linttest.NewSuite(t) test.LoadStubs = []string{"stubs/phpstorm-stubs/standard/standard_1.php"} test.AddFile(`'); } @@ -58,6 +61,7 @@ func TestStripTagsString2(t *testing.T) { test := linttest.NewSuite(t) test.LoadStubs = []string{"stubs/phpstorm-stubs/standard/standard_1.php"} test.AddFile(`

'); } diff --git a/src/tests/checkers/trait_test.go b/src/tests/checkers/trait_test.go index 884e37274..167d820bf 100644 --- a/src/tests/checkers/trait_test.go +++ b/src/tests/checkers/trait_test.go @@ -9,6 +9,7 @@ import ( func TestTraitSingleton(t *testing.T) { // See #533. linttest.SimpleNegativeTest(t, ` 1, 'b' => 2, 'c' => 3]; if (array_key_exists('z', $array)) { // 2 diff --git a/src/tests/golden/testdata/quickfix/complexExamples.php.fix.expected b/src/tests/golden/testdata/quickfix/complexExamples.php.fix.expected index 503b05611..3ed4c9e56 100644 --- a/src/tests/golden/testdata/quickfix/complexExamples.php.fix.expected +++ b/src/tests/golden/testdata/quickfix/complexExamples.php.fix.expected @@ -1,5 +1,6 @@ sum(); // actual PHP prints 6 func TestIssue209_2(t *testing.T) { test := linttest.NewSuite(t) test.AddFile(` 1, "\n" => 2]; `) } diff --git a/src/tests/regression/issue6_test.go b/src/tests/regression/issue6_test.go index cad278d7a..128505fae 100644 --- a/src/tests/regression/issue6_test.go +++ b/src/tests/regression/issue6_test.go @@ -8,7 +8,8 @@ import ( func TestIssue6(t *testing.T) { linttest.SimpleNegativeTest(t, `