Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion blink/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ CI keeps it current.
| tool | rules | of ECS |
|---|---:|---:|
| [ECS](https://github.com/symplify/easy-coding-standard) (baseline) | 207 | 100% |
| blink | 211 | 101% |
| blink | 212 | 102% |
<!-- rule-counts:end -->

## Build
Expand Down
1 change: 1 addition & 0 deletions blink/internal/fixer/fixer.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var ecsSrcOnlyFixers = map[string]bool{
`Symplify\CodingStandard\Fixer\Annotation\RemovePropertyVariableNameDescriptionFixer`: true,
`Symplify\CodingStandard\Fixer\Annotation\RemoveMethodNameDuplicateDescriptionFixer`: true,
`Symplify\CodingStandard\Fixer\Annotation\RemoveEventSubscriberDescriptionFixer`: true,
`Symplify\CodingStandard\Fixer\ConstantNotation\RemoveSlashFromNativeConstantFixer`: true,
}

// Fixer inspects a token stream and mutates it in place. It is the core contract
Expand Down
1 change: 1 addition & 0 deletions blink/internal/fixer/rules/priorities.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var fixerPriority = map[string]int{
"FixParamNameTypoFixer": -37,
"RemoveParamNameReferenceFixer": -37,
"RemoveParamDescriptionDuplicateNameFixer": 7,
"RemoveSlashFromNativeConstantFixer": 0,
"SwitchedTypeAndNameFixer": -37,
"RemoveDeadParamFixer": -37,
"RemoveDeadVarThisFixer": -37,
Expand Down
1 change: 1 addition & 0 deletions blink/internal/fixer/rules/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ func StructuralFixers() []fixer.Fixer {
SingleBlankLineBeforeNamespace{},
BlankLineAfterNamespace{},
NoUnusedImports{},
RemoveSlashFromNativeConstant{},
FullyQualifiedStrictTypes{},
SingleImportPerStatement{},
OrderedImports{},
Expand Down
59 changes: 59 additions & 0 deletions blink/internal/fixer/rules/remove_slash_from_native_constant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package rules

import (
"blink/internal/token"
"blink/internal/tokens"
)

// Symplify: https://github.com/ecsphp/ecs-src/blob/main/packages/coding-standard/src/Fixer/ConstantNotation/RemoveSlashFromNativeConstantFixer.php
//
// RemoveSlashFromNativeConstant strips the redundant leading backslash from the
// true, false and null constants (\true -> true).
type RemoveSlashFromNativeConstant struct{}

func (RemoveSlashFromNativeConstant) Name() string {
return `Symplify\CodingStandard\Fixer\ConstantNotation\RemoveSlashFromNativeConstantFixer`
}

func (RemoveSlashFromNativeConstant) SourceURL() string {
return "https://github.com/ecsphp/ecs-src/blob/main/packages/coding-standard/src/Fixer/ConstantNotation/RemoveSlashFromNativeConstantFixer.php"
}

func (RemoveSlashFromNativeConstant) Fix(s *tokens.Stream) bool {
changed := false
for i := 0; i < s.Len(); i++ {
if s.At(i).Kind != token.Punct || s.At(i).Value != `\` {
continue
}

next := nextSignificantIndex(s, i)
if next < 0 || s.At(next).Kind != token.Ident {
continue
}

lower := lowerASCII(s.At(next).Value)
if lower != "true" && lower != "false" && lower != "null" {
continue
}

// the keyword must stand alone, not be a namespace segment like
// "Foo\true" or "\true\Bar"
if prev := prevSignificantIndex(s, i); prev >= 0 {
pt := s.At(prev)
if pt.Kind == token.Ident || (pt.Kind == token.Punct && pt.Value == `\`) {
continue
}
}
if after := nextSignificantIndex(s, next); after >= 0 {
at := s.At(after)
if at.Kind == token.Punct && at.Value == `\` {
continue
}
}

s.RemoveAt(i)
changed = true
i--
}
return changed
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package rules

import "testing"

func TestRemoveSlashFromNativeConstant(t *testing.T) {
t.Parallel()
f := RemoveSlashFromNativeConstant{}

// leading backslash on true/false/null is stripped
got, changed := apply(t, f, "<?php\nif (\\true) {\n return \\false;\n}\nreturn \\null;\n")
if want := "<?php\nif (true) {\n return false;\n}\nreturn null;\n"; !changed || got != want {
t.Fatalf("strip: changed=%v got=%q", changed, got)
}

// casing is preserved, only the slash goes
got, changed = apply(t, f, "<?php\n$x = \\TRUE;\n")
if want := "<?php\n$x = TRUE;\n"; !changed || got != want {
t.Fatalf("casing: changed=%v got=%q", changed, got)
}

// without a backslash: no-op
if _, changed := apply(t, f, "<?php\nreturn true;\n"); changed {
t.Fatal("bare constant must be a no-op")
}

// namespace segment named true/null stays untouched
if _, changed := apply(t, f, "<?php\nreturn Some\\True\\Value::NAME;\n"); changed {
t.Fatal("namespace segment must be a no-op")
}
if _, changed := apply(t, f, "<?php\nreturn \\true\\Bar::NAME;\n"); changed {
t.Fatal("leading segment before more namespace must be a no-op")
}
}
Loading