forked from Wavyzz/dolibarr
Qual: New fixers for phan
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
/* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
|
/* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
|
||||||
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
|
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -9,30 +9,40 @@
|
|||||||
$config = include __DIR__.DIRECTORY_SEPARATOR."config.php";
|
$config = include __DIR__.DIRECTORY_SEPARATOR."config.php";
|
||||||
|
|
||||||
|
|
||||||
|
// Note: When more than one fixer is attached to the same Notice, only the last fix is applied.
|
||||||
//require_once __DIR__.'/plugins/DeprecatedModuleNameFixer.php';
|
//require_once __DIR__.'/plugins/DeprecatedModuleNameFixer.php';
|
||||||
//require_once __DIR__.'/plugins/PriceFormFixer.php';
|
//require_once __DIR__.'/plugins/PriceFormFixer.php';
|
||||||
//require_once __DIR__.'/plugins/UrlEncodeStringifyFixer.php';
|
//require_once __DIR__.'/plugins/UrlEncodeStringifyFixer.php';
|
||||||
require_once __DIR__.'/plugins/SelectDateFixer.php';
|
//require_once __DIR__.'/plugins/SelectDateFixer.php';
|
||||||
|
require_once __DIR__.'/plugins/setPageOrientationFixer.php';
|
||||||
|
//require_once __DIR__.'/plugins/MultiCellFixer.php';
|
||||||
|
//require_once __DIR__.'/plugins/setAutoPageBreakFixer.php';
|
||||||
|
//require_once __DIR__.'/plugins/CellFixer.php';
|
||||||
|
|
||||||
//$deprecatedModuleNameRegex = '/^(?!(?:'.implode('|', array_keys($DEPRECATED_MODULE_MAPPING)).')$).*/';
|
//$deprecatedModuleNameRegex = '/^(?!(?:'.implode('|', array_keys($DEPRECATED_MODULE_MAPPING)).')$).*/';
|
||||||
|
|
||||||
require_once __DIR__.'/plugins/DeprecatedModuleNameFixer.php';
|
//require_once __DIR__.'/plugins/DeprecatedModuleNameFixer.php';
|
||||||
|
|
||||||
$config['exclude_file_regex'] = '@^(' // @phpstan-ignore-line
|
$config['exclude_file_regex'] = '@^(' // @phpstan-ignore-line
|
||||||
.'dummy' // @phpstan-ignore-line
|
.'dummy' // @phpstan-ignore-line
|
||||||
|
//.'|(?!htdocs/modulebuilderrtemplate/core/modules/mymodule/doc/pdf_standard_myobject.modules.php).*' // Only this file for test @php-stan-ignore-line
|
||||||
|
.'|htdocs/custom/.*' // Ignore all custom modules @phpstan-ignore-line
|
||||||
.'|htdocs/.*/canvas/.*/tpl/.*.tpl.php' // @phpstan-ignore-line
|
.'|htdocs/.*/canvas/.*/tpl/.*.tpl.php' // @phpstan-ignore-line
|
||||||
.'|htdocs/admin/tools/ui/.*' // @phpstan-ignore-line
|
.'|htdocs/admin/tools/ui/.*' // @phpstan-ignore-line
|
||||||
//.'|htdocs/modulebuilder/template/.*' // @phpstan-ignore-line
|
//.'|htdocs/modulebuilder/template/.*' // @phpstan-ignore-line
|
||||||
|
// Included as stub (better analysis)
|
||||||
|
.'|htdocs/includes/nusoap/.*' // @phpstan-ignore-line
|
||||||
// Included as stub (old version + incompatible typing hints)
|
// Included as stub (old version + incompatible typing hints)
|
||||||
.'|htdocs/includes/restler/.*' // @phpstan-ignore-line
|
.'|htdocs/includes/restler/.*' // @phpstan-ignore-line
|
||||||
// Included as stub (did not seem properly analysed by phan without it)
|
// Included as stub (did not seem properly analyzed by phan without it)
|
||||||
.'|htdocs/includes/stripe/.*' // @phpstan-ignore-line
|
.'|htdocs/includes/stripe/.*' // @phpstan-ignore-line
|
||||||
.'|htdocs/conf/conf.php' // @phpstan-ignore-line
|
.'|htdocs/conf/conf.php' // @phpstan-ignore-line
|
||||||
//.'|htdocs/[^c][^o][^r][^e][^/].*' // For testing @phpstan-ignore-line
|
//.'|htdocs/[^mi](?!.*(pdf_|tcpdf)).*\.php' // @phpstan-ignore-line
|
||||||
//.'|htdocs/[^h].*' // For testing on restricted set @phpstan-ignore-line
|
//.'|htdocs/(?!.*modules.*(pdf_|pdf.lib)).*\.php' // @phpstan-ignore-line
|
||||||
.')@'; // @phpstan-ignore-line
|
.')@'; // @phpstan-ignore-line
|
||||||
|
|
||||||
// $config['plugins'][] = __DIR__.'/plugins/ParamMatchRegexPlugin.php';
|
// $config['plugins'][] = __DIR__.'/plugins/ParamMatchRegexPlugin.php';
|
||||||
|
$config['plugins'] = [];
|
||||||
$config['plugins'][] = 'DeprecateAliasPlugin';
|
$config['plugins'][] = 'DeprecateAliasPlugin';
|
||||||
// $config['plugins'][] = __DIR__.'/plugins/GetPostFixerPlugin.php';
|
// $config['plugins'][] = __DIR__.'/plugins/GetPostFixerPlugin.php';
|
||||||
// $config['plugins'][] = 'PHPDocToRealTypesPlugin';
|
// $config['plugins'][] = 'PHPDocToRealTypesPlugin';
|
||||||
|
|||||||
185
dev/tools/phan/plugins/CellFixer.php
Normal file
185
dev/tools/phan/plugins/CellFixer.php
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
<?php
|
||||||
|
/* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* For 'price()', replace $form parameter that is '' with 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use ast\flags;
|
||||||
|
use Microsoft\PhpParser\Node\Expression\CallExpression;
|
||||||
|
use Microsoft\PhpParser\Node\QualifiedName;
|
||||||
|
use Phan\AST\TolerantASTConverter\NodeUtils;
|
||||||
|
use Phan\CodeBase;
|
||||||
|
use Phan\IssueInstance;
|
||||||
|
use Phan\Library\FileCacheEntry;
|
||||||
|
use Phan\Plugin\Internal\IssueFixingPlugin\FileEdit;
|
||||||
|
use Phan\Plugin\Internal\IssueFixingPlugin\FileEditSet;
|
||||||
|
use Phan\Plugin\Internal\IssueFixingPlugin\IssueFixer;
|
||||||
|
use Microsoft\PhpParser\Node\Expression\ArgumentExpression;
|
||||||
|
use Microsoft\PhpParser\Node\DelimitedList\ArgumentExpressionList;
|
||||||
|
use Microsoft\PhpParser\Node\StringLiteral;
|
||||||
|
use Microsoft\PhpParser\Node\NumericLiteral;
|
||||||
|
use Microsoft\PhpParser\Node\ReservedWord;
|
||||||
|
use Microsoft\PhpParser\Token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a prototype, there are various features it does not implement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
call_user_func(static function (): void {
|
||||||
|
/**
|
||||||
|
* @param $code_base @unused-param
|
||||||
|
* @return ?FileEditSet a representation of the edit to make to replace a call to a function alias with a call to the original function
|
||||||
|
*/
|
||||||
|
$fix = static function (CodeBase $code_base, FileCacheEntry $contents, IssueInstance $instance): ?FileEditSet {
|
||||||
|
|
||||||
|
// Argument {INDEX} (${PARAMETER}) is {CODE} of type {TYPE}{DETAILS} but
|
||||||
|
// {FUNCTIONLIKE} takes {TYPE}{DETAILS} defined at {FILE}:{LINE} (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
||||||
|
|
||||||
|
//htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 3 ($h) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
||||||
|
//htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 4 ($m) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
||||||
|
// var_dump($instance->getTemplateParameters());
|
||||||
|
$argument_index = (string) $instance->getTemplateParameters()[0];
|
||||||
|
$argument_name = (string) $instance->getTemplateParameters()[1];
|
||||||
|
$argument_code = (string) $instance->getTemplateParameters()[2];
|
||||||
|
$argument_type = (string) $instance->getTemplateParameters()[3];
|
||||||
|
$functionlike = (string) $instance->getTemplateParameters()[4];
|
||||||
|
$functiontype = (string) $instance->getTemplateParameters()[5];
|
||||||
|
|
||||||
|
$expected_functionlike = "\\TCPDI::Cell()";
|
||||||
|
$expected_functionlike2 = "\\TCPDF::Cell()";
|
||||||
|
$expected_name = "Cell";
|
||||||
|
if ($functionlike !== $expected_functionlike
|
||||||
|
&& $functionlike !== $expected_functionlike2) {
|
||||||
|
print "$functionlike != '$expected_functionlike'|'$expected_functionlike2".PHP_EOL;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$toBoolReplaceArray = array("0" => "false","1" => "true");
|
||||||
|
// Check if we fix any of this
|
||||||
|
if (
|
||||||
|
($argument_name === 'fill' && in_array($argument_code, array_keys($toBoolReplaceArray)))
|
||||||
|
//|| ($argument_name === 'm' && $argument_code === "''")
|
||||||
|
//|| ($argument_name === 'empty' && $argument_code === "''")
|
||||||
|
) {
|
||||||
|
$replacement = $toBoolReplaceArray[$argument_code];
|
||||||
|
$argIdx = ($argument_index - 1) * 2;
|
||||||
|
$expectedStringValue = $argument_code;
|
||||||
|
} else {
|
||||||
|
print "ARG$argument_index:$argument_name CODE:$argument_name".PHP_EOL;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we established that the notification
|
||||||
|
// matches some we fix.
|
||||||
|
|
||||||
|
$line = $instance->getLine();
|
||||||
|
|
||||||
|
$edits = [];
|
||||||
|
foreach ($contents->getNodesAtLine($line) as $node) {
|
||||||
|
if (!$node instanceof ArgumentExpressionList) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$arguments = $node->children;
|
||||||
|
if (count($arguments) <= $argIdx) {
|
||||||
|
print "Arg Count is ".count($arguments)." - Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_actual_call = $node->parent instanceof CallExpression;
|
||||||
|
if (!$is_actual_call) {
|
||||||
|
print "Not actual call - Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
print "Actual call - $instance".PHP_EOL;
|
||||||
|
$callable = $node->parent;
|
||||||
|
|
||||||
|
$callableExpression = $callable->callableExpression;
|
||||||
|
|
||||||
|
if ($callableExpression instanceof Microsoft\PhpParser\Node\QualifiedName) {
|
||||||
|
$actual_name = $callableExpression->getResolvedName();
|
||||||
|
} elseif ($callableExpression instanceof Microsoft\PhpParser\Node\Expression\MemberAccessExpression) {
|
||||||
|
$memberNameToken = $callableExpression->memberName;
|
||||||
|
$actual_name = (new NodeUtils($contents->getContents()))->tokenToString($memberNameToken);
|
||||||
|
} else {
|
||||||
|
print "Callable expression is ".get_class($callableExpression)."- Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((string) $actual_name !== (string) $expected_name) {
|
||||||
|
// print "Name unexpected '$actual_name'!='$expected_name' - Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($arguments as $i => $argument) {
|
||||||
|
if ($argument instanceof ArgumentExpression) {
|
||||||
|
print "Type$i: ".get_class($argument->expression).PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$fieldValue = null;
|
||||||
|
|
||||||
|
|
||||||
|
$arg = $arguments[$argIdx];
|
||||||
|
|
||||||
|
if (
|
||||||
|
$arg instanceof ArgumentExpression
|
||||||
|
// && $arg->expression instanceof StringLiteral
|
||||||
|
&& $arg->expression instanceof NumericLiteral
|
||||||
|
) {
|
||||||
|
// Get the value of the NumericLiteral
|
||||||
|
$fieldValue = (string) $arg->expression;
|
||||||
|
print "Number is '$fieldValue'".PHP_EOL;
|
||||||
|
/*
|
||||||
|
// Get the string value of the StringLiteral
|
||||||
|
$fieldValue = $arg->expression->getStringContentsText();
|
||||||
|
print "String is '$fieldValue'".PHP_EOL;
|
||||||
|
*/
|
||||||
|
} elseif ($arg instanceof ArgumentExpression && $arg->expression instanceof ReservedWord) {
|
||||||
|
$child = $arg->expression->children;
|
||||||
|
if (!$child instanceof Token) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$token_str = (new NodeUtils($contents->getContents()))->tokenToString($child);
|
||||||
|
print "$token_str KIND:".($child->kind ?? 'no kind')." ".get_class($child).PHP_EOL;
|
||||||
|
|
||||||
|
if ($token_str !== 'null') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fieldValue = ''; // Fake empty
|
||||||
|
} else {
|
||||||
|
print "Expression is not expected type ".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fieldValue !== $expectedStringValue) {
|
||||||
|
print "Not replacing $argument_name which is '$fieldValue'/".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
print "Fixture elem on $line - $actual_name(...'$fieldValue'...) - $instance".PHP_EOL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Get the first argument (delimiter)
|
||||||
|
$argument_to_replace = $arg;
|
||||||
|
|
||||||
|
$arg_start_pos = $argument_to_replace->getStartPosition();
|
||||||
|
$arg_end_pos = $argument_to_replace->getEndPosition();
|
||||||
|
|
||||||
|
// Set edit instruction
|
||||||
|
$edits[] = new FileEdit($arg_start_pos, $arg_end_pos, $replacement);
|
||||||
|
}
|
||||||
|
if ($edits) {
|
||||||
|
return new FileEditSet($edits);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
IssueFixer::registerFixerClosure(
|
||||||
|
'PhanTypeMismatchArgument',
|
||||||
|
$fix
|
||||||
|
);
|
||||||
|
});
|
||||||
185
dev/tools/phan/plugins/MultiCellFixer.php
Normal file
185
dev/tools/phan/plugins/MultiCellFixer.php
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
<?php
|
||||||
|
/* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* For 'price()', replace $form parameter that is '' with 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use ast\flags;
|
||||||
|
use Microsoft\PhpParser\Node\Expression\CallExpression;
|
||||||
|
use Microsoft\PhpParser\Node\QualifiedName;
|
||||||
|
use Phan\AST\TolerantASTConverter\NodeUtils;
|
||||||
|
use Phan\CodeBase;
|
||||||
|
use Phan\IssueInstance;
|
||||||
|
use Phan\Library\FileCacheEntry;
|
||||||
|
use Phan\Plugin\Internal\IssueFixingPlugin\FileEdit;
|
||||||
|
use Phan\Plugin\Internal\IssueFixingPlugin\FileEditSet;
|
||||||
|
use Phan\Plugin\Internal\IssueFixingPlugin\IssueFixer;
|
||||||
|
use Microsoft\PhpParser\Node\Expression\ArgumentExpression;
|
||||||
|
use Microsoft\PhpParser\Node\DelimitedList\ArgumentExpressionList;
|
||||||
|
use Microsoft\PhpParser\Node\StringLiteral;
|
||||||
|
use Microsoft\PhpParser\Node\NumericLiteral;
|
||||||
|
use Microsoft\PhpParser\Node\ReservedWord;
|
||||||
|
use Microsoft\PhpParser\Token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a prototype, there are various features it does not implement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
call_user_func(static function (): void {
|
||||||
|
/**
|
||||||
|
* @param $code_base @unused-param
|
||||||
|
* @return ?FileEditSet a representation of the edit to make to replace a call to a function alias with a call to the original function
|
||||||
|
*/
|
||||||
|
$fix = static function (CodeBase $code_base, FileCacheEntry $contents, IssueInstance $instance): ?FileEditSet {
|
||||||
|
|
||||||
|
// Argument {INDEX} (${PARAMETER}) is {CODE} of type {TYPE}{DETAILS} but
|
||||||
|
// {FUNCTIONLIKE} takes {TYPE}{DETAILS} defined at {FILE}:{LINE} (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
||||||
|
|
||||||
|
//htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 3 ($h) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
||||||
|
//htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 4 ($m) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
||||||
|
// var_dump($instance->getTemplateParameters());
|
||||||
|
$argument_index = (string) $instance->getTemplateParameters()[0];
|
||||||
|
$argument_name = (string) $instance->getTemplateParameters()[1];
|
||||||
|
$argument_code = (string) $instance->getTemplateParameters()[2];
|
||||||
|
$argument_type = (string) $instance->getTemplateParameters()[3];
|
||||||
|
$functionlike = (string) $instance->getTemplateParameters()[4];
|
||||||
|
$functiontype = (string) $instance->getTemplateParameters()[5];
|
||||||
|
|
||||||
|
$expected_functionlike = "\\TCPDI::MultiCell()";
|
||||||
|
$expected_functionlike2 = "\\TCPDF::MultiCell()";
|
||||||
|
$expected_name = "MultiCell";
|
||||||
|
if ($functionlike !== $expected_functionlike
|
||||||
|
&& $functionlike !== $expected_functionlike2) {
|
||||||
|
print "$functionlike != '$expected_functionlike'|'$expected_functionlike2".PHP_EOL;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$toBoolReplaceArray = array("0" => "false","1" => "true");
|
||||||
|
// Check if we fix any of this
|
||||||
|
if (
|
||||||
|
($argument_name === 'fill' && in_array($argument_code, array_keys($toBoolReplaceArray)))
|
||||||
|
//|| ($argument_name === 'm' && $argument_code === "''")
|
||||||
|
//|| ($argument_name === 'empty' && $argument_code === "''")
|
||||||
|
) {
|
||||||
|
$replacement = $toBoolReplaceArray[$argument_code];
|
||||||
|
$argIdx = ($argument_index - 1) * 2;
|
||||||
|
$expectedStringValue = $argument_code;
|
||||||
|
} else {
|
||||||
|
print "ARG$argument_index:$argument_name CODE:$argument_name".PHP_EOL;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we established that the notification
|
||||||
|
// matches some we fix.
|
||||||
|
|
||||||
|
$line = $instance->getLine();
|
||||||
|
|
||||||
|
$edits = [];
|
||||||
|
foreach ($contents->getNodesAtLine($line) as $node) {
|
||||||
|
if (!$node instanceof ArgumentExpressionList) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$arguments = $node->children;
|
||||||
|
if (count($arguments) <= $argIdx) {
|
||||||
|
print "Arg Count is ".count($arguments)." - Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_actual_call = $node->parent instanceof CallExpression;
|
||||||
|
if (!$is_actual_call) {
|
||||||
|
print "Not actual call - Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
print "Actual call - $instance".PHP_EOL;
|
||||||
|
$callable = $node->parent;
|
||||||
|
|
||||||
|
$callableExpression = $callable->callableExpression;
|
||||||
|
|
||||||
|
if ($callableExpression instanceof Microsoft\PhpParser\Node\QualifiedName) {
|
||||||
|
$actual_name = $callableExpression->getResolvedName();
|
||||||
|
} elseif ($callableExpression instanceof Microsoft\PhpParser\Node\Expression\MemberAccessExpression) {
|
||||||
|
$memberNameToken = $callableExpression->memberName;
|
||||||
|
$actual_name = (new NodeUtils($contents->getContents()))->tokenToString($memberNameToken);
|
||||||
|
} else {
|
||||||
|
print "Callable expression is ".get_class($callableExpression)."- Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((string) $actual_name !== (string) $expected_name) {
|
||||||
|
// print "Name unexpected '$actual_name'!='$expected_name' - Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($arguments as $i => $argument) {
|
||||||
|
if ($argument instanceof ArgumentExpression) {
|
||||||
|
print "Type$i: ".get_class($argument->expression).PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$fieldValue = null;
|
||||||
|
|
||||||
|
|
||||||
|
$arg = $arguments[$argIdx];
|
||||||
|
|
||||||
|
if (
|
||||||
|
$arg instanceof ArgumentExpression
|
||||||
|
// && $arg->expression instanceof StringLiteral
|
||||||
|
&& $arg->expression instanceof NumericLiteral
|
||||||
|
) {
|
||||||
|
// Get the value of the NumericLiteral
|
||||||
|
$fieldValue = (string) $arg->expression;
|
||||||
|
print "Number is '$fieldValue'".PHP_EOL;
|
||||||
|
/*
|
||||||
|
// Get the string value of the StringLiteral
|
||||||
|
$fieldValue = $arg->expression->getStringContentsText();
|
||||||
|
print "String is '$fieldValue'".PHP_EOL;
|
||||||
|
*/
|
||||||
|
} elseif ($arg instanceof ArgumentExpression && $arg->expression instanceof ReservedWord) {
|
||||||
|
$child = $arg->expression->children;
|
||||||
|
if (!$child instanceof Token) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$token_str = (new NodeUtils($contents->getContents()))->tokenToString($child);
|
||||||
|
print "$token_str KIND:".($child->kind ?? 'no kind')." ".get_class($child).PHP_EOL;
|
||||||
|
|
||||||
|
if ($token_str !== 'null') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fieldValue = ''; // Fake empty
|
||||||
|
} else {
|
||||||
|
print "Expression is not expected type ".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fieldValue !== $expectedStringValue) {
|
||||||
|
print "Not replacing $argument_name which is '$fieldValue'/".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
print "Fixture elem on $line - $actual_name(...'$fieldValue'...) - $instance".PHP_EOL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Get the first argument (delimiter)
|
||||||
|
$argument_to_replace = $arg;
|
||||||
|
|
||||||
|
$arg_start_pos = $argument_to_replace->getStartPosition();
|
||||||
|
$arg_end_pos = $argument_to_replace->getEndPosition();
|
||||||
|
|
||||||
|
// Set edit instruction
|
||||||
|
$edits[] = new FileEdit($arg_start_pos, $arg_end_pos, $replacement);
|
||||||
|
}
|
||||||
|
if ($edits) {
|
||||||
|
return new FileEditSet($edits);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
IssueFixer::registerFixerClosure(
|
||||||
|
'PhanTypeMismatchArgument',
|
||||||
|
$fix
|
||||||
|
);
|
||||||
|
});
|
||||||
185
dev/tools/phan/plugins/setAutoPageBreakFixer.php
Normal file
185
dev/tools/phan/plugins/setAutoPageBreakFixer.php
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
<?php
|
||||||
|
/* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* For 'price()', replace $form parameter that is '' with 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use ast\flags;
|
||||||
|
use Microsoft\PhpParser\Node\Expression\CallExpression;
|
||||||
|
use Microsoft\PhpParser\Node\QualifiedName;
|
||||||
|
use Phan\AST\TolerantASTConverter\NodeUtils;
|
||||||
|
use Phan\CodeBase;
|
||||||
|
use Phan\IssueInstance;
|
||||||
|
use Phan\Library\FileCacheEntry;
|
||||||
|
use Phan\Plugin\Internal\IssueFixingPlugin\FileEdit;
|
||||||
|
use Phan\Plugin\Internal\IssueFixingPlugin\FileEditSet;
|
||||||
|
use Phan\Plugin\Internal\IssueFixingPlugin\IssueFixer;
|
||||||
|
use Microsoft\PhpParser\Node\Expression\ArgumentExpression;
|
||||||
|
use Microsoft\PhpParser\Node\DelimitedList\ArgumentExpressionList;
|
||||||
|
use Microsoft\PhpParser\Node\StringLiteral;
|
||||||
|
use Microsoft\PhpParser\Node\NumericLiteral;
|
||||||
|
use Microsoft\PhpParser\Node\ReservedWord;
|
||||||
|
use Microsoft\PhpParser\Token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a prototype, there are various features it does not implement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
call_user_func(static function (): void {
|
||||||
|
/**
|
||||||
|
* @param $code_base @unused-param
|
||||||
|
* @return ?FileEditSet a representation of the edit to make to replace a call to a function alias with a call to the original function
|
||||||
|
*/
|
||||||
|
$fix = static function (CodeBase $code_base, FileCacheEntry $contents, IssueInstance $instance): ?FileEditSet {
|
||||||
|
|
||||||
|
// Argument {INDEX} (${PARAMETER}) is {CODE} of type {TYPE}{DETAILS} but
|
||||||
|
// {FUNCTIONLIKE} takes {TYPE}{DETAILS} defined at {FILE}:{LINE} (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
||||||
|
|
||||||
|
//htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 3 ($h) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
||||||
|
//htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 4 ($m) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
||||||
|
// var_dump($instance->getTemplateParameters());
|
||||||
|
$argument_index = (string) $instance->getTemplateParameters()[0];
|
||||||
|
$argument_name = (string) $instance->getTemplateParameters()[1];
|
||||||
|
$argument_code = (string) $instance->getTemplateParameters()[2];
|
||||||
|
$argument_type = (string) $instance->getTemplateParameters()[3];
|
||||||
|
$functionlike = (string) $instance->getTemplateParameters()[4];
|
||||||
|
$functiontype = (string) $instance->getTemplateParameters()[5];
|
||||||
|
|
||||||
|
$expected_functionlike = "\\TCPDI::setAutoPageBreak()";
|
||||||
|
$expected_functionlike2 = "\\TCPDF::setAutoPageBreak()";
|
||||||
|
$expected_name = "setAutoPageBreak";
|
||||||
|
if ($functionlike !== $expected_functionlike
|
||||||
|
&& $functionlike !== $expected_functionlike2) {
|
||||||
|
//print "$functionlike != '$expected_functionlike'|'$expected_functionlike2".PHP_EOL;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$toBoolReplaceArray = array("0" => "false","1" => "true");
|
||||||
|
// Check if we fix any of this
|
||||||
|
if (
|
||||||
|
($argument_name === 'auto' && in_array($argument_code, array_keys($toBoolReplaceArray)))
|
||||||
|
//|| ($argument_name === 'm' && $argument_code === "''")
|
||||||
|
//|| ($argument_name === 'empty' && $argument_code === "''")
|
||||||
|
) {
|
||||||
|
$replacement = $toBoolReplaceArray[$argument_code];
|
||||||
|
$argIdx = ($argument_index - 1) * 2;
|
||||||
|
$expectedStringValue = $argument_code;
|
||||||
|
} else {
|
||||||
|
//print "ARG$argument_index:$argument_name CODE:$argument_name".PHP_EOL;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we established that the notification
|
||||||
|
// matches some we fix.
|
||||||
|
|
||||||
|
$line = $instance->getLine();
|
||||||
|
|
||||||
|
$edits = [];
|
||||||
|
foreach ($contents->getNodesAtLine($line) as $node) {
|
||||||
|
if (!$node instanceof ArgumentExpressionList) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$arguments = $node->children;
|
||||||
|
if (count($arguments) <= $argIdx) {
|
||||||
|
// print "Arg Count is ".count($arguments)." - Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_actual_call = $node->parent instanceof CallExpression;
|
||||||
|
if (!$is_actual_call) {
|
||||||
|
//print "Not actual call - Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//print "Actual call - $instance".PHP_EOL;
|
||||||
|
$callable = $node->parent;
|
||||||
|
|
||||||
|
$callableExpression = $callable->callableExpression;
|
||||||
|
|
||||||
|
if ($callableExpression instanceof Microsoft\PhpParser\Node\QualifiedName) {
|
||||||
|
$actual_name = $callableExpression->getResolvedName();
|
||||||
|
} elseif ($callableExpression instanceof Microsoft\PhpParser\Node\Expression\MemberAccessExpression) {
|
||||||
|
$memberNameToken = $callableExpression->memberName;
|
||||||
|
$actual_name = (new NodeUtils($contents->getContents()))->tokenToString($memberNameToken);
|
||||||
|
} else {
|
||||||
|
//print "Callable expression is ".get_class($callableExpression)."- Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((string) $actual_name !== (string) $expected_name) {
|
||||||
|
print "Name unexpected '$actual_name'!='$expected_name' - Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($arguments as $i => $argument) {
|
||||||
|
if ($argument instanceof ArgumentExpression) {
|
||||||
|
//print "Type$i: ".get_class($argument->expression).PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$fieldValue = null;
|
||||||
|
|
||||||
|
|
||||||
|
$arg = $arguments[$argIdx];
|
||||||
|
|
||||||
|
if (
|
||||||
|
$arg instanceof ArgumentExpression
|
||||||
|
// && $arg->expression instanceof StringLiteral
|
||||||
|
&& $arg->expression instanceof NumericLiteral
|
||||||
|
) {
|
||||||
|
// Get the value of the NumericLiteral
|
||||||
|
$fieldValue = (string) $arg->expression;
|
||||||
|
print "Number is '$fieldValue'".PHP_EOL;
|
||||||
|
/*
|
||||||
|
// Get the string value of the StringLiteral
|
||||||
|
$fieldValue = $arg->expression->getStringContentsText();
|
||||||
|
print "String is '$fieldValue'".PHP_EOL;
|
||||||
|
*/
|
||||||
|
} elseif ($arg instanceof ArgumentExpression && $arg->expression instanceof ReservedWord) {
|
||||||
|
$child = $arg->expression->children;
|
||||||
|
if (!$child instanceof Token) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$token_str = (new NodeUtils($contents->getContents()))->tokenToString($child);
|
||||||
|
print "$token_str KIND:".($child->kind ?? 'no kind')." ".get_class($child).PHP_EOL;
|
||||||
|
|
||||||
|
if ($token_str !== 'null') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fieldValue = ''; // Fake empty
|
||||||
|
} else {
|
||||||
|
print "Expression is not expected type ".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fieldValue !== $expectedStringValue) {
|
||||||
|
print "Not replacing '$argument_name' which is '$fieldValue'/".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
print "Fixture elem on $line - $actual_name(...'$fieldValue'...) - $instance".PHP_EOL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Get the first argument (delimiter)
|
||||||
|
$argument_to_replace = $arg;
|
||||||
|
|
||||||
|
$arg_start_pos = $argument_to_replace->getStartPosition();
|
||||||
|
$arg_end_pos = $argument_to_replace->getEndPosition();
|
||||||
|
|
||||||
|
// Set edit instruction
|
||||||
|
$edits[] = new FileEdit($arg_start_pos, $arg_end_pos, $replacement);
|
||||||
|
}
|
||||||
|
if ($edits) {
|
||||||
|
return new FileEditSet($edits);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
IssueFixer::registerFixerClosure(
|
||||||
|
'PhanTypeMismatchArgument',
|
||||||
|
$fix
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
/* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
|
/* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
|
||||||
*
|
*
|
||||||
* For 'price()', replace $form parameter that is '' with 0.
|
* For 'price()', replace $form parameter that is '' with 0.
|
||||||
*/
|
*/
|
||||||
@@ -19,6 +19,7 @@ use Phan\Plugin\Internal\IssueFixingPlugin\IssueFixer;
|
|||||||
use Microsoft\PhpParser\Node\Expression\ArgumentExpression;
|
use Microsoft\PhpParser\Node\Expression\ArgumentExpression;
|
||||||
use Microsoft\PhpParser\Node\DelimitedList\ArgumentExpressionList;
|
use Microsoft\PhpParser\Node\DelimitedList\ArgumentExpressionList;
|
||||||
use Microsoft\PhpParser\Node\StringLiteral;
|
use Microsoft\PhpParser\Node\StringLiteral;
|
||||||
|
use Microsoft\PhpParser\Node\NumericLiteral;
|
||||||
use Microsoft\PhpParser\Node\ReservedWord;
|
use Microsoft\PhpParser\Node\ReservedWord;
|
||||||
use Microsoft\PhpParser\Token;
|
use Microsoft\PhpParser\Token;
|
||||||
|
|
||||||
@@ -38,31 +39,31 @@ call_user_func(static function (): void {
|
|||||||
|
|
||||||
//htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 3 ($h) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
//htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 3 ($h) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
||||||
//htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 4 ($m) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
//htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 4 ($m) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type)
|
||||||
|
// var_dump($instance->getTemplateParameters());
|
||||||
$argument_index = (string) $instance->getTemplateParameters()[0];
|
$argument_index = (string) $instance->getTemplateParameters()[0];
|
||||||
$argument_name = (string) $instance->getTemplateParameters()[1];
|
$argument_name = (string) $instance->getTemplateParameters()[1];
|
||||||
$argument_code = (string) $instance->getTemplateParameters()[2];
|
$argument_code = (string) $instance->getTemplateParameters()[2];
|
||||||
$argument_type = (string) $instance->getTemplateParameters()[3];
|
$argument_type = (string) $instance->getTemplateParameters()[3];
|
||||||
$details = (string) $instance->getTemplateParameters()[4];
|
$functionlike = (string) $instance->getTemplateParameters()[4];
|
||||||
$functionlike = (string) $instance->getTemplateParameters()[5];
|
$functiontype = (string) $instance->getTemplateParameters()[5];
|
||||||
|
|
||||||
$expected_functionlike = "\\TCPDI::setPageOrientation()";
|
$expected_functionlike = "\\TCPDF::setPageOrientation()";
|
||||||
$expected_name = "setPageOrientation";
|
$expected_name = "setPageOrientation";
|
||||||
if ($functionlike !== $expected_functionlike) {
|
if ($functionlike !== $expected_functionlike) {
|
||||||
print "$functionlike != '$expected_functionlike'".PHP_EOL;
|
//print "$functionlike != '$expected_functionlike'".PHP_EOL;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$toBoolReplaceArray=array("0"=>"false","1"=>"true");
|
$toBoolReplaceArray = array("0" => "false","1" => "true");
|
||||||
// Check if we fix any of this
|
// Check if we fix any of this
|
||||||
if (
|
if (
|
||||||
($argument_name === 'argument_name' && in_array($argument_code, array_keys($toBoolReplaceArray)))
|
($argument_name === 'autopagebreak' && in_array($argument_code, array_keys($toBoolReplaceArray)))
|
||||||
//|| ($argument_name === 'm' && $argument_code === "''")
|
//|| ($argument_name === 'm' && $argument_code === "''")
|
||||||
//|| ($argument_name === 'empty' && $argument_code === "''")
|
//|| ($argument_name === 'empty' && $argument_code === "''")
|
||||||
) {
|
) {
|
||||||
$replacement = $toBoolReplaceArray[$argument_name];
|
$replacement = $toBoolReplaceArray[$argument_code];
|
||||||
$argIdx = ($argument_index - 1) * 2;
|
$argIdx = ($argument_index - 1) * 2;
|
||||||
$expectedStringValue = "";
|
$expectedStringValue = $argument_code;
|
||||||
} else {
|
} else {
|
||||||
print "ARG$argument_index:$argument_name CODE:$argument_name".PHP_EOL;
|
print "ARG$argument_index:$argument_name CODE:$argument_name".PHP_EOL;
|
||||||
return null;
|
return null;
|
||||||
@@ -112,22 +113,22 @@ call_user_func(static function (): void {
|
|||||||
|
|
||||||
foreach ($arguments as $i => $argument) {
|
foreach ($arguments as $i => $argument) {
|
||||||
if ($argument instanceof ArgumentExpression) {
|
if ($argument instanceof ArgumentExpression) {
|
||||||
print "Type$i: ".get_class($argument->expression).PHP_EOL;
|
// print "Type$i: ".get_class($argument->expression).PHP_EOL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$stringValue = null;
|
$fieldValue = null;
|
||||||
|
|
||||||
|
|
||||||
$arg = $arguments[$argIdx];
|
$arg = $arguments[$argIdx];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
$arg instanceof ArgumentExpression
|
$arg instanceof ArgumentExpression
|
||||||
&& $arg->expression instanceof StringLiteral
|
&& $arg->expression instanceof NumericLiteral
|
||||||
) {
|
) {
|
||||||
// Get the string value of the StringLiteral
|
// Get the string value of the NumericLiteral
|
||||||
$stringValue = $arg->expression->getStringContentsText();
|
$fieldValue = (string) $arg->expression;
|
||||||
print "String is '$stringValue'".PHP_EOL;
|
//print "Field is '$fieldValue'".PHP_EOL;
|
||||||
} elseif ($arg instanceof ArgumentExpression && $arg->expression instanceof ReservedWord) {
|
} elseif ($arg instanceof ArgumentExpression && $arg->expression instanceof ReservedWord) {
|
||||||
$child = $arg->expression->children;
|
$child = $arg->expression->children;
|
||||||
if (!$child instanceof Token) {
|
if (!$child instanceof Token) {
|
||||||
@@ -140,18 +141,18 @@ call_user_func(static function (): void {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stringValue = ''; // Fake empty
|
$fieldValue = ''; // Fake empty
|
||||||
} else {
|
} else {
|
||||||
print "Expression is not string or null ".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL;
|
// print "Expression is not expected type ".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($stringValue !== $expectedStringValue) {
|
if ($fieldValue !== $expectedStringValue) {
|
||||||
print "Not replacing $argument_name which is '$stringValue'/".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL;
|
// print "Not replacing '$argument_name' which is '$fieldValue'/".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
print "Fixture elem on $line - $actual_name(...'$stringValue'...) - $instance".PHP_EOL;
|
print "Fixture elem on $line - $actual_name(...'$fieldValue'...) - $instance".PHP_EOL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user