Skip to content

Commit

Permalink
Merge pull request #16 from magento/NovemberInfraSync
Browse files Browse the repository at this point in the history
 Updating To latest Infra SVC code changes
  • Loading branch information
magterskine authored Nov 13, 2019
2 parents c236238 + 76d4fd4 commit aadb409
Show file tree
Hide file tree
Showing 23 changed files with 729 additions and 401 deletions.
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

0 comments on commit aadb409

Please sign in to comment.