Skip to content

Commit

Permalink
linter: split undefined checker into the following (#1092)
Browse files Browse the repository at this point in the history
- maybeUndefined
- undefinedConstant
- undefinedFunction
- undefinedMethod
- undefinedProperty
- undefinedType
- undefinedVariable

* tests: fixed baseline
  • Loading branch information
i582 authored Aug 21, 2021
1 parent 17cd256 commit 0334dd7
Show file tree
Hide file tree
Showing 16 changed files with 259 additions and 60 deletions.
151 changes: 140 additions & 11 deletions docs/checkers_doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

| Total checks | Checks enabled by default | Disabled checks by default | Autofixable checks |
| ------------ | ------------------------- | -------------------------- | ------------------ |
| 89 | 79 | 10 | 12 |
| 95 | 85 | 10 | 12 |
## Table of contents
- Enabled by default
- [`accessLevel` checker](#accesslevel-checker)
Expand Down Expand Up @@ -48,6 +48,7 @@
- [`keywordCase` checker](#keywordcase-checker)
- [`linterError` checker](#lintererror-checker)
- [`magicMethodDecl` checker](#magicmethoddecl-checker)
- [`maybeUndefined` checker](#maybeundefined-checker)
- [`methodSignatureMismatch` checker](#methodsignaturemismatch-checker)
- [`misspellComment` checker](#misspellcomment-checker)
- [`misspellName` checker](#misspellname-checker)
Expand Down Expand Up @@ -81,7 +82,12 @@
- [`syntax` checker](#syntax-checker)
- [`ternarySimplify` checker (autofixable)](#ternarysimplify-checker)
- [`unaryRepeat` checker (autofixable)](#unaryrepeat-checker)
- [`undefined` checker](#undefined-checker)
- [`undefinedConstant` checker](#undefinedconstant-checker)
- [`undefinedFunction` checker](#undefinedfunction-checker)
- [`undefinedMethod` checker](#undefinedmethod-checker)
- [`undefinedProperty` checker](#undefinedproperty-checker)
- [`undefinedType` checker](#undefinedtype-checker)
- [`undefinedVariable` checker](#undefinedvariable-checker)
- [`unimplemented` checker](#unimplemented-checker)
- [`unused` checker](#unused-checker)
- [`varShadow` checker](#varshadow-checker)
Expand Down Expand Up @@ -940,6 +946,31 @@ class Foo {
<p><br></p>


### `maybeUndefined` checker

#### Description

Report usages of potentially undefined symbols.

#### Non-compliant code:
```php
if ($cond) {
$v = 10;
}
return $v; // $v may be undefined.
```

#### Compliant code:
```php
$v = 0; // Default value.
if ($cond) {
$v = 10;
}
return $v;
```
<p><br></p>


### `methodSignatureMismatch` checker

#### Description
Expand Down Expand Up @@ -1591,29 +1622,127 @@ echo (bool) $a;
<p><br></p>


### `undefined` checker
### `undefinedConstant` checker

#### Description

Report usages of potentially undefined symbols.
Report usages of undefined constant.

#### Non-compliant code:
```php
if ($cond) {
$v = 10;
echo PI;
```

#### Compliant code:
```php
echo M_PI;
```
<p><br></p>


### `undefinedFunction` checker

#### Description

Report usages of undefined function.

#### Non-compliant code:
```php
undefinedFunc();
```

#### Compliant code:
```php
definedFunc();
```
<p><br></p>


### `undefinedMethod` checker

#### Description

Report usages of undefined method.

#### Non-compliant code:
```php
class Foo {
public function method() {};
}
return $v; // $v may be undefined.

(new Foo)->method2(); // method2 is undefined.
```

#### Compliant code:
```php
$v = 0; // Default value.
if ($cond) {
$v = 10;
class Foo {
public function method() {}
}
return $v;

(new Foo)->method();
```
<p><br></p>


### `undefinedProperty` checker

#### Description

Report usages of undefined property.

#### Non-compliant code:
```php
class Foo {
public string $prop;
}

(new Foo)->prop2; // prop2 is undefined.
```

#### Compliant code:
```php
class Foo {
public string $prop;
}

(new Foo)->prop;
```
<p><br></p>


### `undefinedType` checker

#### Description

Report usages of undefined type.

#### Non-compliant code:
```php
class Foo extends UndefinedClass {}
```

#### Compliant code:
```php
class Foo extends DefinedClass {}
```
<p><br></p>


### `undefinedVariable` checker

#### Description

Report usages of undefined variable.

#### Non-compliant code:
```php
echo $undefinedVar;
```

#### Compliant code:
```php
$definedVar = 100;
echo $definedVar;
```
<p><br></p>

Expand Down
2 changes: 1 addition & 1 deletion src/linter/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -1236,7 +1236,7 @@ func (b *blockWalker) enterClosure(fun *ir.ClosureExpr, haveThis bool, thisType
}

if !b.ctx.sc.HaveVar(v) && !byRef {
b.r.Report(v, LevelWarning, "undefined", "Undefined variable $%s", v.Name)
b.r.Report(v, LevelWarning, "undefinedVariable", "Undefined variable $%s", v.Name)
}

typ, ok := b.ctx.sc.GetVarNameType(v.Name)
Expand Down
14 changes: 7 additions & 7 deletions src/linter/block_linter.go
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ func (b *blockLinter) checkConstFetch(e *ir.ConstFetchExpr) {
b.report(e.Constant, LevelError, "constCase", "Constant '%s' should be used in lower case as '%s'", nm, expected)
b.addFixForBuiltInConstantCase(e.Constant, expected)
default:
b.report(e.Constant, LevelError, "undefined", "Undefined constant %s", nm)
b.report(e.Constant, LevelError, "undefinedConstant", "Undefined constant %s", nm)
}
}
}
Expand Down Expand Up @@ -942,7 +942,7 @@ func (b *blockLinter) checkDeprecatedFunctionCall(e *ir.FunctionCallExpr, call *

func (b *blockLinter) checkFunctionAvailability(e *ir.FunctionCallExpr, call *funcCallInfo) {
if !call.isFound && !b.walker.ctx.customFunctionExists(e.Function) {
b.report(e.Function, LevelError, "undefined", "Call to undefined function %s", utils.NameNodeToString(e.Function))
b.report(e.Function, LevelError, "undefinedFunction", "Call to undefined function %s", utils.NameNodeToString(e.Function))
}
}

Expand Down Expand Up @@ -1175,7 +1175,7 @@ func (b *blockLinter) checkMethodCall(e *ir.MethodCallExpr) {
// The method is undefined, but we permit calling it if `method_exists`
// was called prior to that call.
if !b.walker.ctx.customMethodExists(e.Variable, call.methodName) && needShowUndefinedMethod {
b.report(e.Method, LevelError, "undefined", "Call to undefined method {%s}->%s()", call.methodCallerType, call.methodName)
b.report(e.Method, LevelError, "undefinedMethod", "Call to undefined method {%s}->%s()", call.methodCallerType, call.methodName)
}
} else if !call.isMagic && !parseState.IsTrait {
// Method is defined.
Expand Down Expand Up @@ -1213,7 +1213,7 @@ func (b *blockLinter) checkStaticCall(e *ir.StaticCallExpr) {
}

if !call.isFound && !call.isMagic && !b.classParseState().IsTrait {
b.report(e.Call, LevelError, "undefined", "Call to undefined method %s::%s()", call.className, call.methodName)
b.report(e.Call, LevelError, "undefinedMethod", "Call to undefined method %s::%s()", call.className, call.methodName)
} else if !call.isParentCall && !call.methodInfo.Info.IsStatic() && !call.isMagic && !b.classParseState().IsTrait {
// Method is defined.
// parent::f() is permitted.
Expand Down Expand Up @@ -1247,7 +1247,7 @@ func (b *blockLinter) checkPropertyFetch(e *ir.PropertyFetchExpr) {
!b.classParseState().IsTrait &&
!b.walker.isThisInsideClosure(e.Variable) &&
needShowUndefinedProperty {
b.report(e.Property, LevelError, "undefined", "Property {%s}->%s does not exist", fetch.propertyFetchType, fetch.propertyNode.Value)
b.report(e.Property, LevelError, "undefinedProperty", "Property {%s}->%s does not exist", fetch.propertyFetchType, fetch.propertyNode.Value)
}

if fetch.isFound && !fetch.isMagic && !canAccess(b.classParseState(), fetch.className, fetch.info.AccessLevel) {
Expand All @@ -1264,7 +1264,7 @@ func (b *blockLinter) checkStaticPropertyFetch(e *ir.StaticPropertyFetchExpr) {
b.checkClassSpecialNameCase(e, fetch.className)

if !fetch.isFound && !b.classParseState().IsTrait {
b.report(e.Property, LevelError, "undefined", "Property %s::$%s does not exist", fetch.className, fetch.propertyName)
b.report(e.Property, LevelError, "undefinedProperty", "Property %s::$%s does not exist", fetch.className, fetch.propertyName)
}

if fetch.isFound && !canAccess(b.classParseState(), fetch.info.ClassName, fetch.info.Info.AccessLevel) {
Expand All @@ -1288,7 +1288,7 @@ func (b *blockLinter) checkClassConstFetch(e *ir.ClassConstFetchExpr) {
}

if !fetch.isFound && !b.classParseState().IsTrait {
b.walker.r.Report(e.ConstantName, LevelError, "undefined", "Class constant %s::%s does not exist", fetch.className, fetch.constName)
b.walker.r.Report(e.ConstantName, LevelError, "undefinedConstant", "Class constant %s::%s does not exist", fetch.className, fetch.constName)
}

if fetch.isFound && !canAccess(b.classParseState(), fetch.implClassName, fetch.info.AccessLevel) {
Expand Down
79 changes: 74 additions & 5 deletions src/linter/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,21 +344,90 @@ public function process($acts, $config) {
},

{
Name: "undefined",
Name: "undefinedProperty",
Default: true,
Quickfix: false,
Comment: `Report usages of undefined property.`,
Before: `class Foo {
public string $prop;
}
(new Foo)->prop2; // prop2 is undefined.`,
After: `class Foo {
public string $prop;
}
(new Foo)->prop;`,
},

{
Name: "undefinedMethod",
Default: true,
Quickfix: false,
Comment: `Report usages of undefined method.`,
Before: `class Foo {
public function method() {};
}
(new Foo)->method2(); // method2 is undefined.`,
After: `class Foo {
public function method() {}
}
(new Foo)->method();`,
},

{
Name: "undefinedConstant",
Default: true,
Quickfix: false,
Comment: `Report usages of undefined constant.`,
Before: `echo PI;`,
After: `echo M_PI;`,
},

{
Name: "undefinedFunction",
Default: true,
Quickfix: false,
Comment: `Report usages of undefined function.`,
Before: `undefinedFunc();`,
After: `definedFunc();`,
},

{
Name: "undefinedVariable",
Default: true,
Quickfix: false,
Comment: `Report usages of undefined variable.`,
Before: `echo $undefinedVar;`,
After: `$definedVar = 100;
echo $definedVar;`,
},

{
Name: "maybeUndefined",
Default: true,
Quickfix: false,
Comment: `Report usages of potentially undefined symbols.`,
Before: `if ($cond) {
$v = 10;
}
return $v; // $v may be undefined.
`,
return $v; // $v may be undefined.`,
After: `$v = 0; // Default value.
if ($cond) {
$v = 10;
}
return $v;
`,
return $v;`,
},

{
Name: "undefinedType",
Default: true,
Quickfix: false,
Comment: `Report usages of undefined type.`,
Before: `class Foo extends UndefinedClass {}`,
After: `class Foo extends DefinedClass {}`,
},

{
Expand Down
8 changes: 4 additions & 4 deletions src/linter/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ func (d *rootWalker) reportHash(loc *ir.Location, contextLine []byte, checkName,
func (d *rootWalker) reportUndefinedVariable(v ir.Node, maybeHave bool) {
sv, ok := v.(*ir.SimpleVar)
if !ok {
d.Report(v, LevelWarning, "undefined", "Unknown variable variable %s used",
d.Report(v, LevelWarning, "undefinedVariable", "Unknown variable variable %s used",
utils.NameNodeToString(v))
return
}
Expand All @@ -579,9 +579,9 @@ func (d *rootWalker) reportUndefinedVariable(v ir.Node, maybeHave bool) {
}

if maybeHave {
d.Report(sv, LevelWarning, "undefined", "Variable $%s might have not been defined", sv.Name)
d.Report(sv, LevelWarning, "maybeUndefined", "Variable $%s might have not been defined", sv.Name)
} else {
d.Report(sv, LevelError, "undefined", "Undefined variable $%s", sv.Name)
d.Report(sv, LevelError, "undefinedVariable", "Undefined variable $%s", sv.Name)
}
}

Expand Down Expand Up @@ -2309,7 +2309,7 @@ func (d *rootWalker) checkImplementedStep(classNode, name ir.Node, className str
}

func (d *rootWalker) reportUndefinedType(n ir.Node, name string) {
d.Report(n, LevelError, "undefined", "Type %s not found", name)
d.Report(n, LevelError, "undefinedType", "Type %s not found", name)
}

func (d *rootWalker) checkNameCase(n ir.Node, nameUsed, nameExpected string) {
Expand Down
Loading

0 comments on commit 0334dd7

Please sign in to comment.