Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Generic/UpperCaseConstantName: improve code coverage #665

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -87,37 +87,39 @@ public function process(File $phpcsFile, $stackPtr)
return;
}

// Make sure this is not a method call.
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
// Make sure this is not a method call or class instantiation.
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($tokens[$prev]['code'] === T_OBJECT_OPERATOR
|| $tokens[$prev]['code'] === T_DOUBLE_COLON
|| $tokens[$prev]['code'] === T_NULLSAFE_OBJECT_OPERATOR
|| $tokens[$prev]['code'] === T_NEW
) {
return;
}

// Make sure this is not an attribute.
if (empty($tokens[$stackPtr]['nested_attributes']) === false) {
return;
}

// If the next non-whitespace token after this token
// is not an opening parenthesis then it is not a function call.
$openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($openBracket === false) {
if ($openBracket === false || $tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
return;
}

// The next non-whitespace token must be the constant name.
$constPtr = $phpcsFile->findNext(T_WHITESPACE, ($openBracket + 1), null, true);
if ($tokens[$constPtr]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
// Bow out if next non-empty token after the opening parenthesis is not a string (the
// constant name). This could happen when live coding, if the constant is a variable or an
// expression, or if handling a first-class callable or a function definition outside the
// global scope.
$constPtr = $phpcsFile->findNext(Tokens::$emptyTokens, ($openBracket + 1), null, true);
if ($constPtr === false || $tokens[$constPtr]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
return;
}

$constName = $tokens[$constPtr]['content'];

// Check for constants like self::CONSTANT.
$prefix = '';
$splitPos = strpos($constName, '::');
if ($splitPos !== false) {
$prefix = substr($constName, 0, ($splitPos + 2));
$constName = substr($constName, ($splitPos + 2));
}
$prefix = '';

// Strip namespace from constant like /foo/bar/CONSTANT.
$splitPos = strrpos($constName, '\\');
Expand All @@ -128,19 +130,19 @@ public function process(File $phpcsFile, $stackPtr)

if (strtoupper($constName) !== $constName) {
if (strtolower($constName) === $constName) {
$phpcsFile->recordMetric($stackPtr, 'Constant name case', 'lower');
$phpcsFile->recordMetric($constPtr, 'Constant name case', 'lower');
} else {
$phpcsFile->recordMetric($stackPtr, 'Constant name case', 'mixed');
$phpcsFile->recordMetric($constPtr, 'Constant name case', 'mixed');
}

$error = 'Constants must be uppercase; expected %s but found %s';
$data = [
$prefix.strtoupper($constName),
$prefix.$constName,
];
$phpcsFile->addError($error, $stackPtr, 'ConstantNotUpperCase', $data);
$phpcsFile->addError($error, $constPtr, 'ConstantNotUpperCase', $data);
} else {
$phpcsFile->recordMetric($stackPtr, 'Constant name case', 'upper');
$phpcsFile->recordMetric($constPtr, 'Constant name case', 'upper');
}

}//end process()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php
use Exception as My_Exception, foo\bar, baz;
namespace foo;
namespace foo\bar;
namespace bar\foo\baz;

define('VALID_NAME', true);
DEFINE('invalidName', true);
define("VALID_NAME", true);
define("invalidName", true);
define('bar\foo\baz\VALID_NAME_WITH_NAMESPACE', true);
define('bar\foo\baz\invalidNameWithNamespace', true);
define("bar\foo\baz\VALID_NAME_WITH_NAMESPACE", true);
define("bar\foo\baz\invalidNameWithNamespace", true);

class TestClass extends MyClass implements MyInterface, YourInterface
{

const const1 = 'hello';
const CONST2 = 'hello';
}

$foo->define('bar');
$foo->getBar()->define('foo');
Foo::define('bar');

class ClassConstBowOutTest {
const /* comment */ abc = 1;
const // phpcs:ignore Standard.Category.Sniff
some_constant = 2;
}

$foo->getBar()?->define('foo');

// PHP 8.3 introduces typed constants.
class TypedConstants {

const MyClass MYCONST = new MyClass;
const int VALID_NAME = 0;
final public const INT invalid_name = 0;
const FALSE false = false; // Yes, false can be used as a constant name, don't ask.
final protected const array ARRAY = array(); // Same goes for array.
}

define /* comment */ ( /* comment */ 'CommentsInUnconventionalPlaces', 'value' );

define
// comment
(
// phpcs:ignore Stnd.Cat.SniffName -- for reasons.
'CommentsInUnconventionalPlaces',
'value'
);

$foo-> /* comment */ define('bar');
$foo?->
// phpcs:ignore Stnd.Cat.SniffName -- for reasons.
define('bar');

const DEFINE = 'value';

#[Define('some param')]
class MyClass {}

#[
AttributeA,
define('some param')
]
class MyClass {}

const MixedCase = 1;

define('lower_case_name', 'value');
define($var, 'sniff should bow out');
define(constantName(), 'sniff should bow out');
define($obj->constantName(), 'sniff should bow out');
define(MyClass::constantName(), 'sniff should bow out');
define(condition() ? 'name1' : 'name2', 'sniff should bow out');

$callable = define(...);

// Valid if outside the global namespace. Sniff should bow out.
function define($param) {}

class MyClass {
public function define($param) {}
}

$a = ($cond) ? DEFINE : SOMETHING_ELSE;

$object = new Define('value');
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

// Intentional parse error (missing constant name).
// This should be the only test in this file.
// Testing that the sniff is *not* triggered.

define(
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

// Intentional parse error (missing opening parenthesis).
// This should be the only test in this file.
// Testing that the sniff is *not* triggered.

define
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

// Intentional parse error (missing constant name).
// This should be the only test in this file.
// Testing that the sniff is *not* triggered.

const =
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

// Intentional parse error (missing class constant value).
// This should be the only test in this file.
// Testing that the sniff is *not* triggered.

class TypedConstants {
const MISSING_VALUE;
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,32 @@ final class UpperCaseConstantNameUnitTest extends AbstractSniffUnitTest
* The key of the array should represent the line number and the value
* should represent the number of errors that should occur on that line.
*
* @param string $testFile The name of the test file to process.
*
* @return array<int, int>
*/
public function getErrorList()
public function getErrorList($testFile='')
{
return [
8 => 1,
10 => 1,
12 => 1,
14 => 1,
19 => 1,
28 => 1,
30 => 1,
40 => 1,
41 => 1,
];
switch ($testFile) {
case 'UpperCaseConstantNameUnitTest.1.inc':
return [
8 => 1,
10 => 1,
12 => 1,
14 => 1,
19 => 1,
28 => 1,
30 => 1,
40 => 1,
41 => 1,
45 => 1,
51 => 1,
71 => 1,
73 => 1,
];
default:
return [];
}

}//end getErrorList()

Expand Down