Skip to content

Commit

Permalink
added new conditions, refactoring, instanceOf, functions params check…
Browse files Browse the repository at this point in the history
…ing, extends, methods, interfaces, traits, new phpDoc, switch-case, new tests
  • Loading branch information
Hidanio committed Apr 23, 2024
1 parent 2e08a06 commit 60fd646
Show file tree
Hide file tree
Showing 3 changed files with 375 additions and 10 deletions.
40 changes: 40 additions & 0 deletions src/linter/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,9 +613,49 @@ func (b *blockWalker) checkFunAttribute(fun *ir.FunctionStmt) {
}
}

func (b *blockWalker) checkFunctionParamUseSection(method *ir.FunctionStmt) {
returnType, ok := method.ReturnType.(*ir.Name)

if ok {
className := returnType.Value
for _, use := range b.linter.walker.r.useList {
var useName = use.pointer.Use.Value
var alias = ""
if use.pointer.Alias != nil {
alias = use.pointer.Alias.Value
}

if strings.Contains(useName, className) || (alias != "" && strings.Contains(alias, className)) {
b.linter.walker.r.useList["\\"+useName] = UsePair{true, b.linter.walker.r.useList["\\"+useName].pointer}
}
}
}

for _, param := range method.Params {
parameter, _ := param.(*ir.Parameter)
variableType, ok := parameter.VariableType.(*ir.Name)
if ok {
variableTypeName := variableType.Value
for _, use := range b.linter.walker.r.useList {
var useName = use.pointer.Use.Value
var alias = ""
if use.pointer.Alias != nil {
alias = use.pointer.Alias.Value
}

if strings.Contains(useName, variableTypeName) || (alias != "" && strings.Contains(alias, variableTypeName)) {
b.linter.walker.r.useList["\\"+useName] = UsePair{true, b.linter.walker.r.useList["\\"+useName].pointer}
}
}
}

}
}

func (b *blockWalker) handleFunction(fun *ir.FunctionStmt) bool {
b.checkFunParamsAttribute(fun)
b.checkFunAttribute(fun)
b.checkFunctionParamUseSection(fun)

if b.ignoreFunctionBodies {
return false
Expand Down
184 changes: 174 additions & 10 deletions src/linter/block_linter.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ func (b *blockLinter) enterNode(n ir.Node) {
case *ir.ClassStmt:
b.checkClass(n)

case *ir.InstanceOfExpr:
b.checkInstanceOfUseSection(n)

case *ir.FunctionCallExpr:
b.checkFunctionCall(n)

Expand Down Expand Up @@ -207,34 +210,174 @@ func (b *blockLinter) checkUnaryPlus(n *ir.UnaryPlusExpr) {
b.report(n, LevelWarning, "strangeCast", "Unary plus with non-constant expression, possible type cast, use an explicit cast to int or float instead of using the unary plus")
}

func (b *blockLinter) checkClass(class *ir.ClassStmt) {
const classMethod = 0
const classOtherMember = 1
func (b *blockLinter) checkInstanceOfUseSection(expr *ir.InstanceOfExpr) {
classResolve, ok := expr.Class.(*ir.Name)
if !ok {
return
}
className := classResolve.Value

for _, use := range b.walker.r.useList {
var useName = use.pointer.Use.Value
var alias = ""
if use.pointer.Alias != nil {
alias = use.pointer.Alias.Value
}

if strings.Contains(useName, className) || (alias != "" && strings.Contains(alias, className)) {
b.walker.r.useList["\\"+useName] = UsePair{true, b.walker.r.useList["\\"+useName].pointer}
}
}
}

func (b *blockLinter) checkUseSectionPhpDocInClass(class *ir.ClassStmt) {
if b.walker.r.useList == nil {
return
}

doc, found := irutil.FindPHPDoc(class, true)
if !found {
return
}

for _, use := range b.walker.r.useList {
useName := use.pointer.Use.Value
var index = strings.LastIndex(useName, "\\")
var alias = ""
if use.pointer.Alias != nil {
alias = use.pointer.Alias.Value
}

var phpDocMixinFindUse = "@mixin " + useName[index+1:]
var phpDocSeeFindUse = "@see " + useName[index+1:]
if strings.Contains(doc, phpDocMixinFindUse) || (alias != "" && strings.Contains(doc, "@mixin "+alias)) || strings.Contains(doc, phpDocSeeFindUse) {
b.walker.r.useList["\\"+useName] = UsePair{true, b.walker.r.useList["\\"+useName].pointer}
}
}
}

func (b *blockLinter) checkClassUseSectionInExtends(extends *ir.ClassExtendsStmt) {
if extends == nil {
return
}

var className = extends.ClassName.Value
for _, use := range b.walker.r.useList {
var useName = use.pointer.Use.Value
var alias = ""
if use.pointer.Alias != nil {
alias = use.pointer.Alias.Value
}

if strings.Contains(useName, className) || (alias != "" && strings.Contains(alias, className)) {
b.walker.r.useList["\\"+useName] = UsePair{true, b.walker.r.useList["\\"+useName].pointer}
}
}
}

func (b *blockLinter) checkClassMethodUseSection(method *ir.ClassMethodStmt) {
returnType, ok := method.ReturnType.(*ir.Name)
if ok {
className := returnType.Value
for _, use := range b.walker.r.useList {
var useName = use.pointer.Use.Value
var alias = ""
if use.pointer.Alias != nil {
alias = use.pointer.Alias.Value
}

if strings.Contains(useName, className) || (alias != "" && strings.Contains(alias, className)) {
b.walker.r.useList["\\"+useName] = UsePair{true, b.walker.r.useList["\\"+useName].pointer}
}
}
}

if b.walker.r.useList != nil {
doc, found := irutil.FindPHPDoc(class, true)
if found {
for _, param := range method.Params {
parameter, _ := param.(*ir.Parameter)
variableType, ok := parameter.VariableType.(*ir.Name)
if ok {
variableTypeName := variableType.Value
for _, use := range b.walker.r.useList {
useName := use.pointer.Use.Value
var index = strings.LastIndex(useName, "\\")
var useName = use.pointer.Use.Value
var alias = ""
if use.pointer.Alias != nil {
alias = use.pointer.Alias.Value
}

var phpDocFindUse = "@mixin " + useName[index+1:]
if strings.Contains(doc, phpDocFindUse) || (alias != "" && strings.Contains(doc, "@mixin "+alias)) {
if strings.Contains(useName, variableTypeName) || (alias != "" && strings.Contains(alias, variableTypeName)) {
b.walker.r.useList["\\"+useName] = UsePair{true, b.walker.r.useList["\\"+useName].pointer}
}
}
}

}
}

func (b *blockLinter) checkClassInterfacesUseSection(implements *ir.ClassImplementsStmt) {
if implements == nil {
return
}

for _, interf := range implements.InterfaceNames {
interf, _ := interf.(*ir.Name)
interfName := interf.Value
for _, use := range b.walker.r.useList {
var useName = use.pointer.Use.Value
var alias = ""
if use.pointer.Alias != nil {
alias = use.pointer.Alias.Value
}

if strings.Contains(useName, interfName) || (alias != "" && strings.Contains(alias, interfName)) {
b.walker.r.useList["\\"+useName] = UsePair{true, b.walker.r.useList["\\"+useName].pointer}
}
}
}
}

func (b *blockLinter) checkClassTraitsUseSection(class ir.Class) {
if class.Stmts == nil {
return
}

for _, potentialTrait := range class.Stmts {
traits, ok := potentialTrait.(*ir.TraitUseStmt)
if ok {
for _, trait := range traits.Traits {
traitResolve, _ := trait.(*ir.Name)
traitName := traitResolve.Value

for _, use := range b.walker.r.useList {
var useName = use.pointer.Use.Value
var alias = ""
if use.pointer.Alias != nil {
alias = use.pointer.Alias.Value
}

if strings.Contains(useName, traitName) || (alias != "" && strings.Contains(alias, traitName)) {
b.walker.r.useList["\\"+useName] = UsePair{true, b.walker.r.useList["\\"+useName].pointer}
}
}
}
}
}
}

func (b *blockLinter) checkClass(class *ir.ClassStmt) {
const classMethod = 0
const classOtherMember = 1

b.checkUseSectionPhpDocInClass(class)
b.checkClassUseSectionInExtends(class.Extends)
b.checkClassInterfacesUseSection(class.Implements)
b.checkClassTraitsUseSection(class.Class)

var members = make([]int, 0, len(class.Stmts))
for _, stmt := range class.Stmts {
switch stmt.(type) {

Check failure on line 377 in src/linter/block_linter.go

View workflow job for this annotation

GitHub Actions / Build

typeSwitchVar: 1 case can benefit from type switch with assignment (gocritic)
case *ir.ClassMethodStmt:
members = append(members, classMethod)
b.checkClassMethodUseSection(stmt.(*ir.ClassMethodStmt))

Check failure on line 380 in src/linter/block_linter.go

View workflow job for this annotation

GitHub Actions / Build

S1034(related information): could eliminate this type assertion (gosimple)
default:
members = append(members, classOtherMember)
}
Expand Down Expand Up @@ -1436,13 +1579,34 @@ func (b *blockLinter) checkStaticPropertyFetch(e *ir.StaticPropertyFetchExpr) {
}
}

func (b *blockLinter) checkClassConstFetchUseSection(e *ir.ClassConstFetchExpr) {
classResolve, ok := e.Class.(*ir.Name)
if !ok {
return
}

className := classResolve.Value
for _, use := range b.walker.r.useList {
var useName = use.pointer.Use.Value
var alias = ""
if use.pointer.Alias != nil {
alias = use.pointer.Alias.Value
}

if strings.Contains(useName, className) || (alias != "" && strings.Contains(alias, className)) {
b.walker.r.useList["\\"+useName] = UsePair{true, b.walker.r.useList["\\"+useName].pointer}
}
}
}

func (b *blockLinter) checkClassConstFetch(e *ir.ClassConstFetchExpr) {
fetch := resolveClassConstFetch(b.classParseState(), e)
if !fetch.canAnalyze {
return
}

b.checkClassSpecialNameCase(e, fetch.className)
b.checkClassConstFetchUseSection(e)

if !utils.IsSpecialClassName(e.Class) {
usedClassName, ok := solver.GetClassName(b.classParseState(), e.Class)
Expand Down
Loading

0 comments on commit 60fd646

Please sign in to comment.