Skip to content

Updating To latest Infra SVC code changes #16

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 13, 2019
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
40 changes: 14 additions & 26 deletions src/Analyzer/AbstractCodeAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Magento\SemanticVersionChecker\Analyzer;

use Magento\SemanticVersionChecker\ClassHierarchy\DependencyGraph;
use Magento\SemanticVersionChecker\Helper\ClassParser;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
Expand All @@ -22,59 +23,59 @@ abstract class AbstractCodeAnalyzer implements AnalyzerInterface
* @var null|string
*/
protected $context;

/**
* File path before changes.
*
* @var null|string
*/
protected $fileBefore;

/**
* File path after changes.
*
* @var null|string
*/
protected $fileAfter;

/**
* @var null|DependencyGraph
*/
protected $dependencyGraph;
/**
* @param string $context
* @param string $fileBefore
* @param string $fileAfter
* @param DependencyGraph|null $dependencyGraph
*/
public function __construct($context = null, $fileBefore = null, $fileAfter = null)
{
public function __construct(
$context = null,
$fileBefore = null,
$fileAfter = null,
DependencyGraph $dependencyGraph = null
) {
$this->context = $context;
$this->fileBefore = $fileBefore;
$this->fileAfter = $fileAfter;
$this->dependencyGraph = $dependencyGraph;
}

public function analyze($registryBefore, $registryAfter)
{
$report = new Report();

$beforeNameMap = $this->getNodeNameMap($registryBefore);
$afterNameMap = $this->getNodeNameMap($registryAfter);

$namesBefore = array_keys($beforeNameMap);
$namesAfter = array_keys($afterNameMap);
$added = array_diff($namesAfter, $namesBefore);
$removed = array_diff($namesBefore, $namesAfter);
$toVerify = array_intersect($namesBefore, $namesAfter);

$this->reportAdded($report, $registryAfter, $added);
$this->reportMovedOrRemoved($report, $registryBefore, $registryAfter, $removed);

$this->reportChanged(
$report,
$registryBefore,
$registryAfter,
$toVerify
);

return $report;
}

/**
* Gets the appropriate nodes from the context and maps them to their names
*
Expand All @@ -90,7 +91,6 @@ protected function getNodeNameMap($context)
}
return $keyed;
}

/**
* Has the node been moved to parent class.
*
Expand All @@ -101,13 +101,11 @@ protected function getNodeNameMap($context)
protected function isMovedToParent($parsedClass, $removedNode)
{
$parentClass = $parsedClass->getParentClass();

if ($removedNode instanceof ClassLike ||
$parentClass === null ||
(property_exists($removedNode, 'flags') && in_array($removedNode->flags, [Class_::MODIFIER_PRIVATE]))) {
return false;
}

$parentNodes = $parentClass->getNodesOfType($this->getNodeClass());
foreach ($parentNodes as $parentNode) {
$parentNodeName = $this->getNodeName($parentNode);
Expand All @@ -117,25 +115,21 @@ protected function isMovedToParent($parsedClass, $removedNode)
return true;
}
}

return $this->isMovedToParent($parentClass, $removedNode);
}

/**
* Get the name for a given node of the type analyzed
*
* @param Node $node
* @return string
*/
abstract protected function getNodeName($node);

/**
* The class of the nodes to analyze
*
* @return string
*/
abstract protected function getNodeClass();

/**
* Create and report a NodeAdded operation
*
Expand All @@ -146,7 +140,6 @@ abstract protected function getNodeClass();
* @return void
*/
abstract protected function reportAddedNode($report, $fileAfter, $contextAfter, $nodeAfter);

/**
* Create and report a NodeRemoved operation
*
Expand All @@ -157,7 +150,6 @@ abstract protected function reportAddedNode($report, $fileAfter, $contextAfter,
* @return void
*/
abstract protected function reportRemovedNode($report, $fileBefore, $contextBefore, $nodeBefore);

/**
* Create and report a NodeMoved operation
*
Expand All @@ -171,7 +163,6 @@ protected function reportMovedNode($report, $fileBefore, $contextBefore, $nodeBe
{
// ClassLike nodes do not have Moved operations, so do not enforce implementing this method
}

/**
* Report the list of added nodes
*
Expand All @@ -190,7 +181,6 @@ protected function reportAdded($report, $contextAfter, $addedNames)
$this->reportAddedNode($report, $fileAfter, $contextAfter, $node);
}
}

/**
* Report moved or removed nodes
*
Expand All @@ -214,7 +204,6 @@ protected function reportMovedOrRemoved($report, $contextBefore, $contextAfter,
}
}
}

/**
* Find changes to nodes that exist in both before and after states and add them to the report
*
Expand All @@ -228,7 +217,6 @@ protected function reportChanged($report, $contextBefore, $contextAfter, $toVeri
{
// Not all types have changes beyond add/remove
}

/**
* Get the filename to use in the report.
*
Expand All @@ -243,4 +231,4 @@ protected function getFileName($context, $nodeName, $isBefore = true)
{
return $isBefore ? $this->fileBefore : $this->fileAfter;
}
}
}
28 changes: 14 additions & 14 deletions src/Analyzer/ClassAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Magento\SemanticVersionChecker\Analyzer;

use Magento\SemanticVersionChecker\ClassHierarchy\DependencyGraph;
use PhpParser\Node\Stmt\Class_;
use PHPSemVerChecker\Operation\ClassAdded;
use PHPSemVerChecker\Operation\ClassRemoved;
Expand Down Expand Up @@ -45,18 +46,6 @@ protected function getNodeClass()
return Class_::class;
}


/**
* Get the class node registry
*
* @param Registry $registry
* @return Class_[]
*/
protected function getNodeNameMap($registry)
{
return $registry->data[static::CONTEXT];
}

/**
* Get the filename from the registry
*
Expand Down Expand Up @@ -120,7 +109,7 @@ protected function reportChanged($report, $registryBefore, $registryAfter, $toVe
if ($classBefore !== $classAfter) {
/** @var AnalyzerInterface[] $analyzers */
$analyzers = [
new ClassMethodAnalyzer(static::CONTEXT, $fileBefore, $fileAfter),
new ClassMethodAnalyzer(static::CONTEXT, $fileBefore, $fileAfter, $this->dependencyGraph),
new PropertyAnalyzer(static::CONTEXT, $fileBefore, $fileAfter),
new ClassConstantAnalyzer(static::CONTEXT, $fileBefore, $fileAfter),
new ClassMethodExceptionAnalyzer(static::CONTEXT, $fileBefore, $fileAfter),
Expand All @@ -136,4 +125,15 @@ protected function reportChanged($report, $registryBefore, $registryAfter, $toVe
}
}
}
}

/**
* Get the class node registry
*
* @param Registry $registry
* @return Class_[]
*/
protected function getNodeNameMap($registry)
{
return $registry->data[static::CONTEXT];
}
}
105 changes: 99 additions & 6 deletions src/Analyzer/ClassMethodAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,18 @@
use Magento\SemanticVersionChecker\Operation\ClassMethodLastParameterRemoved;
use Magento\SemanticVersionChecker\Operation\ClassMethodMoved;
use Magento\SemanticVersionChecker\Operation\ClassMethodOptionalParameterAdded;
use Magento\SemanticVersionChecker\Operation\ClassMethodOverwriteAdded;
use Magento\SemanticVersionChecker\Operation\ClassMethodOverwriteRemoved;
use Magento\SemanticVersionChecker\Operation\ClassMethodParameterTypingChanged;
use Magento\SemanticVersionChecker\Operation\ClassMethodReturnTypingChanged;
use Magento\SemanticVersionChecker\Operation\ExtendableClassConstructorOptionalParameterAdded;
use Magento\SemanticVersionChecker\Operation\Visibility\MethodDecreased as VisibilityMethodDecreased;
use Magento\SemanticVersionChecker\Operation\Visibility\MethodIncreased as VisibilityMethodIncreased;
use PhpParser\Node\NullableType;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PHPSemVerChecker\Comparator\Implementation;
use PHPSemVerChecker\Comparator\Type;
use PHPSemVerChecker\Operation\ClassMethodAdded;
use PHPSemVerChecker\Operation\ClassMethodImplementationChanged;
use PHPSemVerChecker\Operation\ClassMethodOperationUnary;
Expand All @@ -35,6 +37,12 @@
use PHPSemVerChecker\Operation\ClassMethodParameterTypingRemoved;
use PHPSemVerChecker\Operation\ClassMethodRemoved;
use PHPSemVerChecker\Report\Report;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;

/**
* Class method analyzer.
Expand Down Expand Up @@ -105,6 +113,27 @@ protected function getNodeClass()
*/
protected function reportAddedNode($report, $fileAfter, $classAfter, $methodAfter)
{
if ($this->dependencyGraph === null) {
$report->add($this->context, new ClassMethodAdded($this->context, $fileAfter, $classAfter, $methodAfter));
return;
}

$class = $this->dependencyGraph->findEntityByName((string) $classAfter->namespacedName);

if ($class !== null) {
foreach ($class->getExtends() as $entity) {
$methods = $entity->getMethodList();
// checks if the method is already exiting in parent class
if (isset($methods[$methodAfter->name])) {
$report->add(
$this->context,
new ClassMethodOverwriteAdded($this->context, $fileAfter, $classAfter, $methodAfter)
);
return;
}
}
}

$report->add($this->context, new ClassMethodAdded($this->context, $fileAfter, $classAfter, $methodAfter));
}

Expand Down Expand Up @@ -331,12 +360,76 @@ protected function reportChanged($report, $contextBefore, $contextAfter, $method
*/
private function isReturnTypeChanged(ClassMethod $methodBefore, ClassMethod $methodAfter): bool
{
$hasPHP7ReturnDeclarationChanged = !Type::isSame($methodBefore->returnType, $methodAfter->returnType);
return $this->isDocBlockAnnotationReturnTypeChanged($methodBefore, $methodAfter) || $this->isDeclarationReturnTypeChanged($methodBefore, $methodAfter);
}

/**
* @param ClassMethod $methodBefore
* @param ClassMethod $methodAfter
*
* @return bool
*/
private function isDocBlockAnnotationReturnTypeChanged(ClassMethod $methodBefore, ClassMethod $methodAfter)
{
$returnBefore = $this->getDocReturnDeclaration($methodBefore);
$returnAfter = $this->getDocReturnDeclaration($methodAfter);

$returnBefore = $this->methodDocBlockAnalyzer->getMethodDocDeclarationByTag($methodBefore, $this->methodDocBlockAnalyzer::DOC_RETURN_TAG);
$returnAfter = $this->methodDocBlockAnalyzer->getMethodDocDeclarationByTag($methodAfter, $this->methodDocBlockAnalyzer::DOC_RETURN_TAG);
return $returnBefore !== $returnAfter;
}

return $hasPHP7ReturnDeclarationChanged || $returnBefore !== $returnAfter;
/**
* @param ClassMethod $methodBefore
* @param ClassMethod $methodAfter
*
* @return bool
*/
private function isDeclarationReturnTypeChanged(ClassMethod $methodBefore, ClassMethod $methodAfter)
{
if (!$this->isReturnsEqualByNullability($methodBefore, $methodAfter)) {
return true;
}
$beforeMethodReturnType = $methodBefore->returnType instanceof NullableType ? (string) $methodBefore->returnType->type : (string) $methodBefore->returnType;
$afterMethodReturnType = $methodAfter->returnType instanceof NullableType ? (string) $methodAfter->returnType->type : (string) $methodAfter->returnType;

return $beforeMethodReturnType !== $afterMethodReturnType;
}

/**
* checks if both return types has same nullable status
*
* @param ClassMethod $before
* @param ClassMethod $after
*
* @return bool
*/
private function isReturnsEqualByNullability(ClassMethod $before, ClassMethod $after): bool
{
return ($before instanceof NullableType) === ($after instanceof NullableType);
}

/**
* Analyses the Method doc block and returns the return type declaration
*
* @param ClassMethod $method
*
* @return string
*/
private function getDocReturnDeclaration(ClassMethod $method)
{
if ($method->getDocComment() !== null) {
$lexer = new Lexer();
$typeParser = new TypeParser();
$constExprParser = new ConstExprParser();
$phpDocParser = new PhpDocParser($typeParser, $constExprParser);

$tokens = $lexer->tokenize((string)$method->getDocComment());
$tokenIterator = new TokenIterator($tokens);
$phpDocNode = $phpDocParser->parse($tokenIterator);
$tags = $phpDocNode->getTagsByName('@return');
/** @var PhpDocTagNode $tag */
$tag = array_shift($tags);
}
return isset($tag) ? (string)$tag->value : ' ';
}

/**
Expand Down Expand Up @@ -415,4 +508,4 @@ private function analyzeRemainingMethodParams($contextAfter, $methodAfter, $rema

return $data;
}
}
}
Loading