Skip to content

Commit

Permalink
Qual: Add fixers for price(ARG2), and urlencode((string)...) (Dolibar…
Browse files Browse the repository at this point in the history
…r#28701)

# Qual: Add fixers for price(ARG2), and urlencode((string)...)

Register the fixers used to fix phan messages by adjusting arguments and casts.
  • Loading branch information
mdeweerd authored Mar 8, 2024
1 parent cc53662 commit 6cce456
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 1 deletion.
4 changes: 3 additions & 1 deletion dev/tools/phan/config_fixer.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php

require_once __DIR__.'/plugins/DeprecatedModuleNameFixer.php';
//require_once __DIR__.'/plugins/DeprecatedModuleNameFixer.php';
//require_once __DIR__.'/plugins/PriceFormFixer.php';
require_once __DIR__.'/plugins/UrlEncodeStringifyFixer.php';

/* Copyright (C) 2024 MDW <[email protected]>
*/
Expand Down
138 changes: 138 additions & 0 deletions dev/tools/phan/plugins/PriceFormFixer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php
/* Copyright (C) 2024 MDW <[email protected]>
*
* 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\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 {
$line = $instance->getLine();
// print flags\TYPE_NULL;
$expected_name = 'price';
$edits = [];
foreach ($contents->getNodesAtLine($line) as $node) {
if (!$node instanceof ArgumentExpressionList) {
continue;
}
$arguments = $node->children;
if (count($arguments) < 2) {
// 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();
} 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;
}
}

$stringValue = null;
foreach ([1] as $argIdx) {
$arg = $arguments[$argIdx * 2]; // Pair number to skip "TOKEN" (,)

if (
$arg instanceof ArgumentExpression
&& $arg->expression instanceof StringLiteral
) {
// Get the string value of the StringLiteral
$stringValue = $arg->expression->getStringContentsText();
print "String is '$stringValue'".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;
}

$stringValue = ''; // Fake empty
} else {
print "Expression is not string or null ".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL;
continue;
}

if ($stringValue !== '') {
print "Not replacing \$form which is '$stringValue'/".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL;
continue;
}

// Reached end of switch case without "continue" -> replace
$replacement = 0;

print "Fixture elem on $line - $actual_name(...'$stringValue'...) - $instance".PHP_EOL;

// Determine replacement
$replacement = '0';

// Get the first argument (delimiter)
$argument_to_replace = $arg;

$arg_start_pos = $argument_to_replace->getStartPosition();
$arg_end_pos = $argument_to_replace->getEndPosition();

// Remove deprecated module name
$edits[] = new FileEdit($arg_start_pos, $arg_end_pos, $replacement);
}
}
if ($edits) {
return new FileEditSet($edits);
}
return null;
};
IssueFixer::registerFixerClosure(
'PhanTypeMismatchArgumentProbablyReal',
$fix
);
});
106 changes: 106 additions & 0 deletions dev/tools/phan/plugins/UrlEncodeStringifyFixer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php
/* Copyright (C) 2024 MDW <[email protected]>
*
* 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\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 {
$line = $instance->getLine();
// print flags\TYPE_NULL;
$expected_name = 'urlencode';
$edits = [];
foreach ($contents->getNodesAtLine($line) as $node) {
if (!$node instanceof ArgumentExpressionList) {
continue;
}
$arguments = $node->children;
if (count($arguments) < 1) {
// 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();
} 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;
}
}

$arg = $arguments[0];

// Reached end of switch case without "continue" -> replace
$replacement = 0;

print "Fixture elem on $line - $actual_name() - $instance".PHP_EOL;

// Determine replacement
$replacement = '0';

// Get the first argument (delimiter)
$argument_to_replace = $arg;

$arg_start_pos = $argument_to_replace->getStartPosition();
$arg_end_pos = $argument_to_replace->getEndPosition();

// Remove deprecated module name
$edits[] = new FileEdit($arg_start_pos, $arg_start_pos, "(string) (");
$edits[] = new FileEdit($arg_end_pos, $arg_end_pos, ")");
}
if ($edits) {
return new FileEditSet($edits);
}
return null;
};
IssueFixer::registerFixerClosure(
'PhanTypeMismatchArgumentInternal',
$fix
);
});

0 comments on commit 6cce456

Please sign in to comment.