From a4739430f82063ac0a1800a12bbe94d01fb1128f Mon Sep 17 00:00:00 2001 From: John Nguyen Date: Tue, 22 Aug 2017 15:43:17 +1000 Subject: [PATCH 01/55] Fix memory leak issue (#459) Closes #425 --- src/FqnUtilities.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FqnUtilities.php b/src/FqnUtilities.php index a3aab05..b5d01a9 100644 --- a/src/FqnUtilities.php +++ b/src/FqnUtilities.php @@ -22,7 +22,7 @@ function getFqnsFromType($type): array } if ($type instanceof Types\Compound) { for ($i = 0; $t = $type->get($i); $i++) { - foreach (getFqnsFromType($type) as $fqn) { + foreach (getFqnsFromType($t) as $fqn) { $fqns[] = $fqn; } } From d4443465bb93d2f3cf3013b5c4a3ee482b66f905 Mon Sep 17 00:00:00 2001 From: Stephan Unverwerth Date: Thu, 28 Sep 2017 21:53:12 +0200 Subject: [PATCH 02/55] Fix missing diagnostics for nodes (#484) * Fix missing diagnostics for nodes * Refactor TreeAnalyzer --- src/TreeAnalyzer.php | 92 +++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 32 deletions(-) diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index 2e34aa6..0e43a8c 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -49,44 +49,73 @@ class TreeAnalyzer // TODO - docblock errors - $this->collectDefinitionsAndReferences($this->sourceFileNode); + $this->traverse($this->sourceFileNode); } - private function collectDefinitionsAndReferences(Node $sourceFileNode) + /** + * Collects Parser diagnostic messages for the Node/Token + * and transforms them into LSP Format + * + * @param Node|Token $node + * @return Diagnostic + */ + private function collectDiagnostics($node) { - foreach ($sourceFileNode::CHILD_NAMES as $name) { - $node = $sourceFileNode->$name; + if (($error = PhpParser\DiagnosticsProvider::checkDiagnostics($node)) !== null) { + $range = PhpParser\PositionUtilities::getRangeFromPosition($error->start, $error->length, $this->sourceFileNode->fileContents); - if ($node === null) { - continue; + switch ($error->kind) { + case \Microsoft\PhpParser\DiagnosticKind::Error: + $severity = DiagnosticSeverity::ERROR; + break; + case \Microsoft\PhpParser\DiagnosticKind::Warning: + default: + $severity = DiagnosticSeverity::WARNING; + break; } - if (\is_array($node)) { - foreach ($node as $child) { - if ($child instanceof Node) { - $this->update($child); - } + $this->diagnostics[] = new Diagnostic( + $error->message, + new Range( + new Position($range->start->line, $range->start->character), + new Position($range->end->line, $range->start->character) + ), + null, + $severity, + 'php' + ); + } + } + + /** + * Recursive AST traversal to collect definitions/references and diagnostics + * + * @param Node|Token $currentNode The node/token to process + */ + private function traverse($currentNode) + { + $this->collectDiagnostics($currentNode); + + // Only update/descend into Nodes, Tokens are leaves + if ($currentNode instanceof Node) { + $this->collectDefinitionsAndReferences($currentNode); + + foreach ($currentNode::CHILD_NAMES as $name) { + $child = $currentNode->$name; + + if ($child === null) { + continue; } - continue; - } - if ($node instanceof Node) { - $this->update($node); - } - - if (($error = PhpParser\DiagnosticsProvider::checkDiagnostics($node)) !== null) { - $range = PhpParser\PositionUtilities::getRangeFromPosition($error->start, $error->length, $this->sourceFileNode->fileContents); - - $this->diagnostics[] = new Diagnostic( - $error->message, - new Range( - new Position($range->start->line, $range->start->character), - new Position($range->end->line, $range->start->character) - ), - null, - DiagnosticSeverity::ERROR, - 'php' - ); + if (\is_array($child)) { + foreach ($child as $actualChild) { + if ($actualChild !== null) { + $this->traverse($actualChild); + } + } + } else { + $this->traverse($child); + } } } } @@ -96,7 +125,7 @@ class TreeAnalyzer * * @param Node $node */ - private function update(Node $node) + private function collectDefinitionsAndReferences(Node $node) { $fqn = ($this->definitionResolver)::getDefinedFqn($node); // Only index definitions with an FQN (no variables) @@ -152,7 +181,6 @@ class TreeAnalyzer } } } - $this->collectDefinitionsAndReferences($node); } /** From d24c42008e0e84bb2646e0829f958efdb6e77375 Mon Sep 17 00:00:00 2001 From: Vincent Klaiber Date: Mon, 2 Oct 2017 22:36:04 +0200 Subject: [PATCH 03/55] Exclude non-essential files in .gitattributes (#486) * Exclude non-essential files in .gitattributes https://www.reddit.com/r/PHP/comments/2jzp6k/i_dont_need_your_tests_in_my_production/ * Add validation and .gitmodules --- .gitattributes | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7600d2f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,19 @@ +* text=auto + +/.vscode export-ignore +/fixtures export-ignore +/tests export-ignore +/images export-ignore +/validation export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.gitmodules export-ignore +/.dockerignore export-ignore +/.travis.yml export-ignore +/codecov.yml export-ignore +/Dockerfile export-ignore +/Performance.php export-ignore +/php.ini export-ignore +/phpcs.xml.dist export-ignore +/phpunit.xml.dist export-ignore +/README.md export-ignore From 3d8655d504dd5ba0e6837c4c40180d0b178f5976 Mon Sep 17 00:00:00 2001 From: Vincent Klaiber Date: Mon, 2 Oct 2017 22:37:28 +0200 Subject: [PATCH 04/55] Update phpunit config (#488) * Update phpunit config * Rename DocumentHighlight class --- phpunit.xml.dist | 23 ++++++++++++++++++----- src/Protocol/DocumentHighlight.php | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 38df71a..a6970f3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,14 +1,27 @@ - + - ./tests + ./tests - - - ./src + + ./src diff --git a/src/Protocol/DocumentHighlight.php b/src/Protocol/DocumentHighlight.php index e00c842..167d45a 100644 --- a/src/Protocol/DocumentHighlight.php +++ b/src/Protocol/DocumentHighlight.php @@ -7,7 +7,7 @@ namespace LanguageServer\Protocol; * special attention. Usually a document highlight is visualized by changing * the background color of its range. */ -class DocumentHighlightKind +class DocumentHighlight { /** * The range this highlight applies to. From b9ebfb52c9f6498eb9ac6063b458e07857e89445 Mon Sep 17 00:00:00 2001 From: Vincent Klaiber Date: Mon, 2 Oct 2017 22:58:37 +0200 Subject: [PATCH 05/55] Update composer.json structure (#487) --- composer.json | 58 +++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/composer.json b/composer.json index 50f314b..d28ef31 100644 --- a/composer.json +++ b/composer.json @@ -1,14 +1,7 @@ { "name": "felixfbecker/language-server", "description": "PHP Implementation of the Visual Studio Code Language Server Protocol", - "authors": [ - { - "name": "Felix Becker", - "email": "felix.b@outlook.com" - } - ], "license": "ISC", - "type": "library", "keywords": [ "php", "language", @@ -21,27 +14,30 @@ "autocompletion", "refactor" ], - "bin": ["bin/php-language-server.php"], - "scripts": { - "parse-stubs": "LanguageServer\\ComposerScripts::parseStubs", - "post-install-cmd": "@parse-stubs" - }, + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], "require": { - "php": ">=7.0", + "php": "^7.0", + "composer/composer": "^1.3", + "felixfbecker/advanced-json-rpc": "^2.0", + "jetbrains/phpstorm-stubs": "dev-master", + "microsoft/tolerant-php-parser": "^0.0.4", + "netresearch/jsonmapper": "^1.0", "phpdocumentor/reflection-docblock": "~3.1.1", "sabre/event": "^5.0", - "felixfbecker/advanced-json-rpc": "^2.0", - "squizlabs/php_codesniffer" : "3.0.0RC3", - "netresearch/jsonmapper": "^1.0", - "webmozart/path-util": "^2.3", - "webmozart/glob": "^4.1", "sabre/uri": "^2.0", - "jetbrains/phpstorm-stubs": "dev-master", - "composer/composer": "^1.3", - "Microsoft/tolerant-php-parser": "^0.0.4" + "squizlabs/php_codesniffer": "3.0.0RC3", + "webmozart/glob": "^4.1", + "webmozart/path-util": "^2.3" + }, + "require-dev": { + "phpunit/phpunit": "^5.5", + "phpunit/php-code-coverage": "^4.0" }, - "minimum-stability": "dev", - "prefer-stable": true, "autoload": { "psr-4": { "LanguageServer\\": "src/" @@ -57,8 +53,16 @@ "LanguageServer\\Tests\\": "tests/" } }, - "require-dev": { - "phpunit/phpunit": "^5.5", - "phpunit/php-code-coverage": "^4.0" - } + "bin": [ + "bin/php-language-server.php" + ], + "scripts": { + "parse-stubs": "LanguageServer\\ComposerScripts::parseStubs", + "post-install-cmd": "@parse-stubs" + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev", + "prefer-stable": true } From 0c399150a3e6349f0d1985c025bd099e3bb15206 Mon Sep 17 00:00:00 2001 From: Vincent Klaiber Date: Mon, 2 Oct 2017 23:11:06 +0200 Subject: [PATCH 06/55] Update travis and phpunit (#489) --- .travis.yml | 30 ++++++++++++++++-------------- composer.json | 3 +-- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7fb9c9a..05ebb2a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,28 +1,30 @@ language: php php: - - '7.0' + - 7.0 + - 7.1 + - 7.2 services: - - docker + - docker cache: directories: - - $HOME/.composer/cache + - $HOME/.composer/cache install: - - composer install - - composer run-script parse-stubs + - composer install --prefer-dist --no-interaction + - composer run-script parse-stubs script: - - vendor/bin/phpcs -n - - vendor/bin/phpunit --coverage-clover=coverage.xml + - vendor/bin/phpcs -n + - vendor/bin/phpunit --coverage-clover=coverage.xml after_success: - - bash <(curl -s https://codecov.io/bash) - - | - if [[ $TRAVIS_TAG == v* ]]; then - docker build -t felixfbecker/php-language-server:${TRAVIS_TAG:1} . - docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" - docker push felixfbecker/php-language-server:${TRAVIS_TAG:1} - fi + - bash <(curl -s https://codecov.io/bash) + - | + if [[ $TRAVIS_TAG == v* ]]; then + docker build -t felixfbecker/php-language-server:${TRAVIS_TAG:1} . + docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" + docker push felixfbecker/php-language-server:${TRAVIS_TAG:1} + fi diff --git a/composer.json b/composer.json index d28ef31..c5f6722 100644 --- a/composer.json +++ b/composer.json @@ -35,8 +35,7 @@ "webmozart/path-util": "^2.3" }, "require-dev": { - "phpunit/phpunit": "^5.5", - "phpunit/php-code-coverage": "^4.0" + "phpunit/phpunit": "^6.3" }, "autoload": { "psr-4": { From e31f7b5923a536e3f1162acdbe3cd2014f7610fc Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 19 Oct 2017 14:38:20 -0700 Subject: [PATCH 07/55] Add more Composer scripts --- composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c5f6722..1f62a5a 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,10 @@ ], "scripts": { "parse-stubs": "LanguageServer\\ComposerScripts::parseStubs", - "post-install-cmd": "@parse-stubs" + "post-install-cmd": "@parse-stubs", + "post-update-cmd": "@parse-stubs", + "test": "vendor/bin/phpunit", + "lint": "vendor/bin/phpcs" }, "config": { "sort-packages": true From 19bf94ac7bd28ef5cd9a1dc035b55013f60441c5 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 19 Oct 2017 14:44:56 -0700 Subject: [PATCH 08/55] Improve README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ef351ec..3745ff6 100644 --- a/README.md +++ b/README.md @@ -196,8 +196,8 @@ Then parse the stubs with Run the tests with - vendor/bin/phpunit + composer test Lint with - vendor/bin/phpcs + composer lint From 1240f25e0192f7c5282b86427656e54687cc3278 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 19 Oct 2017 14:45:36 -0700 Subject: [PATCH 09/55] Update parser --- composer.json | 2 +- tests/Validation/cases/stringVariable.php.expected.json | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 1f62a5a..225de3b 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "composer/composer": "^1.3", "felixfbecker/advanced-json-rpc": "^2.0", "jetbrains/phpstorm-stubs": "dev-master", - "microsoft/tolerant-php-parser": "^0.0.4", + "microsoft/tolerant-php-parser": "^0.0.6", "netresearch/jsonmapper": "^1.0", "phpdocumentor/reflection-docblock": "~3.1.1", "sabre/event": "^5.0", diff --git a/tests/Validation/cases/stringVariable.php.expected.json b/tests/Validation/cases/stringVariable.php.expected.json index 61669c8..fed8a23 100644 --- a/tests/Validation/cases/stringVariable.php.expected.json +++ b/tests/Validation/cases/stringVariable.php.expected.json @@ -1,5 +1,9 @@ { - "references": [], + "references": { + "B->hi": [ + "./stringVariable.php" + ] + }, "definitions": { "B": { "fqn": "B", From 7b1176dd9da863c3b094f01a303733ec5faae952 Mon Sep 17 00:00:00 2001 From: "Dependencies.io Bot" Date: Fri, 20 Oct 2017 20:15:31 +0000 Subject: [PATCH 10/55] ci(dependencies.io): add dependencies.yml config --- dependencies.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 dependencies.yml diff --git a/dependencies.yml b/dependencies.yml new file mode 100644 index 0000000..6f8dfc7 --- /dev/null +++ b/dependencies.yml @@ -0,0 +1,13 @@ +collectors: + +- type: php-composer + path: / + actors: + # pull requests for updates to our major version + - type: php-composer + versions: "L.Y.Y" + # create issues for new major versions + - type: repo-issue + versions: "Y.0.0" + settings: + commit_message_prefix: "chore: " From a934aff7a9216824faf4a7f23d5bbff0d42e51de Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 21 Oct 2017 18:39:31 -0700 Subject: [PATCH 11/55] ci(release): use semantic-release --- .dockerignore | 1 + .editorconfig | 2 +- .gitignore | 3 ++- .npmrc | 1 + .travis.yml | 16 ++++++++++------ README.md | 1 + dependencies.yml | 5 +---- package.json | 28 ++++++++++++++++++++++++++++ release-docker.sh | 6 ++++++ 9 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 .npmrc create mode 100644 package.json create mode 100644 release-docker.sh diff --git a/.dockerignore b/.dockerignore index a839c69..29f27b6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,3 +7,4 @@ fixtures/ coverage/ coverage.xml images/ +node_modules/ diff --git a/.editorconfig b/.editorconfig index d162066..2954e68 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,7 +7,7 @@ trim_trailing_whitespace = true indent_style = space indent_size = 4 -[*.json,*.yml] +[*.{json,yml}] indent_size = 2 [composer.json] diff --git a/.gitignore b/.gitignore index f6b089b..6476dbe 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ vendor/ .phpls/ composer.lock stubs -*.ast \ No newline at end of file +*.ast +node_modules/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/.travis.yml b/.travis.yml index 05ebb2a..02e1797 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ services: cache: directories: - $HOME/.composer/cache + - $HOME/.npm install: - composer install --prefer-dist --no-interaction @@ -22,9 +23,12 @@ script: after_success: - bash <(curl -s https://codecov.io/bash) - - | - if [[ $TRAVIS_TAG == v* ]]; then - docker build -t felixfbecker/php-language-server:${TRAVIS_TAG:1} . - docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" - docker push felixfbecker/php-language-server:${TRAVIS_TAG:1} - fi + - git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/* + - git fetch --tags + - nvm install 8 && nvm use 8 + - npm install + - npm run semantic-release + +branches: + except: + - /^v\d+\.\d+\.\d+$/ diff --git a/README.md b/README.md index 3745ff6..0aad4e5 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Build Status](https://travis-ci.org/felixfbecker/php-language-server.svg?branch=master)](https://travis-ci.org/felixfbecker/php-language-server) [![Coverage](https://codecov.io/gh/felixfbecker/php-language-server/branch/master/graph/badge.svg)](https://codecov.io/gh/felixfbecker/php-language-server) [![Dependency Status](https://gemnasium.com/badges/github.com/felixfbecker/php-language-server.svg)](https://gemnasium.com/github.com/felixfbecker/php-language-server) +[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.0-8892BF.svg)](https://php.net/) [![License](https://img.shields.io/packagist/l/felixfbecker/language-server.svg)](https://github.com/felixfbecker/php-language-server/blob/master/LICENSE.txt) [![Gitter](https://badges.gitter.im/felixfbecker/php-language-server.svg)](https://gitter.im/felixfbecker/php-language-server?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) diff --git a/dependencies.yml b/dependencies.yml index 6f8dfc7..320ae87 100644 --- a/dependencies.yml +++ b/dependencies.yml @@ -3,11 +3,8 @@ collectors: - type: php-composer path: / actors: - # pull requests for updates to our major version + # pull requests for new major versions - type: php-composer - versions: "L.Y.Y" - # create issues for new major versions - - type: repo-issue versions: "Y.0.0" settings: commit_message_prefix: "chore: " diff --git a/package.json b/package.json new file mode 100644 index 0000000..d67062f --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "php-language-server", + "version": "0.0.0-development", + "private": true, + "scripts": { + "commitmsg": "validate-commit-msg", + "semantic-release": "semantic-release pre && ./release-docker.sh && semantic-release post" + }, + "devDependencies": { + "cz-conventional-changelog": "^2.0.0", + "husky": "^0.14.3", + "last-release-git": "0.0.3", + "semantic-release": "^8.2.0", + "validate-commit-msg": "^2.14.0" + }, + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" + } + }, + "release": { + "getLastRelease": "last-release-git" + }, + "repository": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server.git" + } +} diff --git a/release-docker.sh b/release-docker.sh new file mode 100644 index 0000000..35e89d9 --- /dev/null +++ b/release-docker.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e + +docker build -t felixfbecker/php-language-server:${TRAVIS_TAG:1} . +docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" +docker push felixfbecker/php-language-server:${TRAVIS_TAG:1} From 4384d494142e3f476481c103b3268b8572f4c7ff Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 22 Oct 2017 17:22:28 -0700 Subject: [PATCH 12/55] ci(travis): remove redundant parse-stubs step --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 02e1797..16dcb76 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,6 @@ cache: install: - composer install --prefer-dist --no-interaction - - composer run-script parse-stubs script: - vendor/bin/phpcs -n From 16cf8f53e9d7106fedef98167d52b21c44cd0bf5 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 22 Oct 2017 17:34:08 -0700 Subject: [PATCH 13/55] fix(docblocks): update to phpdocumentor/reflection-docblock ^4.0.0 closes #139 --- composer.json | 4 ++-- src/Definition.php | 2 +- src/DefinitionResolver.php | 32 ++++++++++++++++---------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/composer.json b/composer.json index 225de3b..0504c00 100644 --- a/composer.json +++ b/composer.json @@ -23,11 +23,11 @@ "require": { "php": "^7.0", "composer/composer": "^1.3", - "felixfbecker/advanced-json-rpc": "^2.0", + "felixfbecker/advanced-json-rpc": "^3.0.0", "jetbrains/phpstorm-stubs": "dev-master", "microsoft/tolerant-php-parser": "^0.0.6", "netresearch/jsonmapper": "^1.0", - "phpdocumentor/reflection-docblock": "~3.1.1", + "phpdocumentor/reflection-docblock": "^4.0.0", "sabre/event": "^5.0", "sabre/uri": "^2.0", "squizlabs/php_codesniffer": "3.0.0RC3", diff --git a/src/Definition.php b/src/Definition.php index c03e852..9a485c4 100644 --- a/src/Definition.php +++ b/src/Definition.php @@ -78,7 +78,7 @@ class Definition * For functions and methods, this is the return type. * For any other declaration it will be null. * Can also be a compound type. - * If it is unknown, will be Types\Mixed. + * If it is unknown, will be Types\Mixed_. * * @var \phpDocumentor\Type|null */ diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 51c5955..dd1040e 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -551,7 +551,7 @@ class DefinitionResolver /** * Given an expression node, resolves that expression recursively to a type. - * If the type could not be resolved, returns Types\Mixed. + * If the type could not be resolved, returns Types\Mixed_. * * @param Node\Expression $expr * @return \phpDocumentor\Reflection\Type|null @@ -567,7 +567,7 @@ class DefinitionResolver if ($expr == null || $expr instanceof PhpParser\MissingToken || $expr instanceof PhpParser\SkippedToken) { // TODO some members are null or Missing/SkippedToken // How do we handle this more generally? - return new Types\Mixed; + return new Types\Mixed_; } // VARIABLE @@ -597,7 +597,7 @@ class DefinitionResolver // Find the function definition if ($expr->callableExpression instanceof Node\Expression) { // Cannot get type for dynamic function call - return new Types\Mixed; + return new Types\Mixed_; } if ($expr->callableExpression instanceof Node\QualifiedName) { @@ -646,7 +646,7 @@ class DefinitionResolver // MEMBER ACCESS EXPRESSION if ($expr instanceof Node\Expression\MemberAccessExpression) { if ($expr->memberName instanceof Node\Expression) { - return new Types\Mixed; + return new Types\Mixed_; } $var = $expr->dereferencableExpression; @@ -659,10 +659,10 @@ class DefinitionResolver if ($t instanceof Types\This) { $classFqn = self::getContainingClassFqn($expr); if ($classFqn === null) { - return new Types\Mixed; + return new Types\Mixed_; } } else if (!($t instanceof Types\Object_) || $t->getFqsen() === null) { - return new Types\Mixed; + return new Types\Mixed_; } else { $classFqn = substr((string)$t->getFqsen(), 1); } @@ -689,7 +689,7 @@ class DefinitionResolver if ($expr instanceof Node\Expression\ScopedPropertyAccessExpression) { $classType = $this->resolveClassNameToType($expr->scopeResolutionQualifier); if (!($classType instanceof Types\Object_) || $classType->getFqsen() === null) { - return new Types\Mixed; + return new Types\Mixed_; } $fqn = substr((string)$classType->getFqsen(), 1) . '::'; @@ -701,7 +701,7 @@ class DefinitionResolver $def = $this->index->getDefinition($fqn); if ($def === null) { - return new Types\Mixed; + return new Types\Mixed_; } return $def->type; } @@ -888,7 +888,7 @@ class DefinitionResolver if ($expr instanceof Node\Expression\SubscriptExpression) { $varType = $this->resolveExpressionNodeToType($expr->postfixExpression); if (!($varType instanceof Types\Array_)) { - return new Types\Mixed; + return new Types\Mixed_; } return $varType->getValueType(); } @@ -897,14 +897,14 @@ class DefinitionResolver // include, require, include_once, require_once if ($expr instanceof Node\Expression\ScriptInclusionExpression) { // TODO: resolve path to PhpDocument and find return statement - return new Types\Mixed; + return new Types\Mixed_; } if ($expr instanceof Node\QualifiedName) { return $this->resolveClassNameToType($expr); } - return new Types\Mixed; + return new Types\Mixed_; } @@ -918,7 +918,7 @@ class DefinitionResolver public function resolveClassNameToType($class): Type { if ($class instanceof Node\Expression) { - return new Types\Mixed; + return new Types\Mixed_; } if ($class instanceof PhpParser\Token && $class->kind === PhpParser\TokenKind::ClassKeyword) { // Anonymous class @@ -958,7 +958,7 @@ class DefinitionResolver * For classes and interfaces, this is the class type (object). * For variables / assignments, this is the documented type or type the assignment resolves to. * Can also be a compound type. - * If it is unknown, will be Types\Mixed. + * If it is unknown, will be Types\Mixed_. * Returns null if the node does not have a type. * * @param Node $node @@ -1012,7 +1012,7 @@ class DefinitionResolver } $type = $defaultType; } - return $type ?? new Types\Mixed; + return $type ?? new Types\Mixed_; } // FUNCTIONS AND METHODS @@ -1040,7 +1040,7 @@ class DefinitionResolver return new Types\Object_(new Fqsen('\\' . (string)$node->returnType->getResolvedName())); } // Unknown return type - return new Types\Mixed; + return new Types\Mixed_; } // PROPERTIES, CONSTS, CLASS CONSTS, ASSIGNMENT EXPRESSIONS @@ -1077,7 +1077,7 @@ class DefinitionResolver // TODO: read @property tags of class // TODO: Try to infer the type from default value / constant value // Unknown - return new Types\Mixed; + return new Types\Mixed_; } // The node does not have a type From 1db6b7bbb3af458feffe356427fae6f85f8a025c Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Sun, 22 Oct 2017 22:54:38 -0700 Subject: [PATCH 14/55] chore: fixes for unused variables and phpdoc (#496) The identifier doesn't need to be generated for a notification to the client, since there's no response Add undeclared properties to TreeAnalyzer Fix other bugs in phpdoc --- src/ClientHandler.php | 1 - src/ComposerScripts.php | 3 ++- src/Server/TextDocument.php | 2 +- src/Server/Workspace.php | 1 + src/TreeAnalyzer.php | 11 +++++++++-- src/utils.php | 3 +-- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/ClientHandler.php b/src/ClientHandler.php index 7b5a702..9fe921b 100644 --- a/src/ClientHandler.php +++ b/src/ClientHandler.php @@ -71,7 +71,6 @@ class ClientHandler */ public function notify(string $method, $params): Promise { - $id = $this->idGenerator->generate(); return $this->protocolWriter->write( new Protocol\Message( new AdvancedJsonRpc\Notification($method, (object)$params) diff --git a/src/ComposerScripts.php b/src/ComposerScripts.php index 487c39d..2a584d2 100644 --- a/src/ComposerScripts.php +++ b/src/ComposerScripts.php @@ -56,7 +56,8 @@ class ComposerScripts $parts['scheme'] = 'phpstubs'; $uri = Uri\build($parts); - $document = new PhpDocument($uri, $content, $index, $parser, $docBlockFactory, $definitionResolver); + // Create a new document and add it to $index + new PhpDocument($uri, $content, $index, $parser, $docBlockFactory, $definitionResolver); } $index->setComplete(); diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index 5a2819e..2bcda2c 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -151,7 +151,7 @@ class TextDocument * The document's truth now exists where the document's uri points to (e.g. if the document's uri is a file uri the * truth now exists on disk). * - * @param \LanguageServer\Protocol\TextDocumentItem $textDocument The document that was closed + * @param \LanguageServer\Protocol\TextDocumentIdentifier $textDocument The document that was closed * @return void */ public function didClose(TextDocumentIdentifier $textDocument) diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index 61db068..fe1dda6 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -123,6 +123,7 @@ class Workspace */ public function xreferences($query, array $files = null): Promise { + // TODO: $files is unused in the coroutine return coroutine(function () use ($query, $files) { if ($this->composerLock === null) { return []; diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index 0e43a8c..81c96e1 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -9,12 +9,19 @@ use phpDocumentor\Reflection\DocBlockFactory; use Sabre\Uri; use Microsoft\PhpParser; use Microsoft\PhpParser\Node; +use Microsoft\PhpParser\Token; class TreeAnalyzer { /** @var PhpParser\Parser */ private $parser; + /** @var DocBlockFactory */ + private $docBlockFactory; + + /** @var DefinitionResolver */ + private $definitionResolver; + /** @var Node\SourceFileNode */ private $sourceFileNode; @@ -57,7 +64,7 @@ class TreeAnalyzer * and transforms them into LSP Format * * @param Node|Token $node - * @return Diagnostic + * @return void */ private function collectDiagnostics($node) { @@ -203,7 +210,7 @@ class TreeAnalyzer } /** - * @return Definition + * @return Definition[] */ public function getDefinitions() { diff --git a/src/utils.php b/src/utils.php index 636b41e..97e091d 100644 --- a/src/utils.php +++ b/src/utils.php @@ -148,9 +148,8 @@ function isVendored(PhpDocument $document, \stdClass $composerJson = null): bool * Check a given URI against the composer.json to see if it * is a vendored URI * - * @param \stdClass|null $composerJson * @param string $uri - * @param array $matches + * @param \stdClass|null $composerJson * @return string|null */ function getPackageName(string $uri, \stdClass $composerJson = null) From fbaa7b3cc536fce0e274743aaf2245a3fce3d5bc Mon Sep 17 00:00:00 2001 From: Jens Hausdorf Date: Sat, 28 Oct 2017 21:27:32 +0200 Subject: [PATCH 15/55] refactor: use ClassLike interface (#506) --- src/DefinitionResolver.php | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index dd1040e..f6d82c5 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -182,9 +182,7 @@ class DefinitionResolver // Interfaces, classes, traits, namespaces, functions, and global const elements $def->isGlobal = ( - $node instanceof Node\Statement\InterfaceDeclaration || - $node instanceof Node\Statement\ClassDeclaration || - $node instanceof Node\Statement\TraitDeclaration || + $node instanceof PhpParser\ClassLike || ($node instanceof Node\Statement\NamespaceDefinition && $node->name !== null) || @@ -1101,9 +1099,7 @@ class DefinitionResolver // interface C { } A\B\C // trait C { } A\B\C if ( - $node instanceof Node\Statement\ClassDeclaration || - $node instanceof Node\Statement\InterfaceDeclaration || - $node instanceof Node\Statement\TraitDeclaration + $node instanceof PhpParser\ClassLike ) { return (string) $node->getNamespacedName(); } @@ -1134,9 +1130,7 @@ class DefinitionResolver // Class method: use ClassName->methodName() as name $class = $node->getFirstAncestor( Node\Expression\ObjectCreationExpression::class, - Node\Statement\ClassDeclaration::class, - Node\Statement\InterfaceDeclaration::class, - Node\Statement\TraitDeclaration::class + PhpParser\ClassLike::class ); if (!isset($class->name)) { // Ignore anonymous classes @@ -1160,9 +1154,7 @@ class DefinitionResolver ($classDeclaration = $node->getFirstAncestor( Node\Expression\ObjectCreationExpression::class, - Node\Statement\ClassDeclaration::class, - Node\Statement\InterfaceDeclaration::class, - Node\Statement\TraitDeclaration::class + PhpParser\ClassLike::class ) ) !== null && isset($classDeclaration->name)) { $name = $node->getName(); @@ -1190,9 +1182,7 @@ class DefinitionResolver // Class constant: use ClassName::CONSTANT_NAME as name $classDeclaration = $constDeclaration->getFirstAncestor( Node\Expression\ObjectCreationExpression::class, - Node\Statement\ClassDeclaration::class, - Node\Statement\InterfaceDeclaration::class, - Node\Statement\TraitDeclaration::class + PhpParser\ClassLike::class ); if (!isset($classDeclaration->name)) { From 95f49d3a706806b9a8d74da1e4f757683ce66ff4 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 28 Oct 2017 13:18:41 -0700 Subject: [PATCH 16/55] ci: set BUILD_LEADER_ID see https://github.com/semantic-release/travis-deploy-once/issues/22 --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 16dcb76..ac0aec5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,10 @@ php: services: - docker +env: + global: + - BUILD_LEADER_ID=1 + cache: directories: - $HOME/.composer/cache From b86d6c96c78f82b0340e2221e01c97450f29ba03 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 28 Oct 2017 13:38:17 -0700 Subject: [PATCH 17/55] build: make release-docker.sh executable --- release-docker.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 release-docker.sh diff --git a/release-docker.sh b/release-docker.sh old mode 100644 new mode 100755 From 9e551a310b5e510989a1bd44777349e98fc132c4 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 28 Oct 2017 13:59:02 -0700 Subject: [PATCH 18/55] build: use PHP for release-docker script --- package.json | 2 +- release-docker.php | 10 ++++++++++ release-docker.sh | 6 ------ 3 files changed, 11 insertions(+), 7 deletions(-) create mode 100755 release-docker.php delete mode 100755 release-docker.sh diff --git a/package.json b/package.json index d67062f..75f02e1 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "commitmsg": "validate-commit-msg", - "semantic-release": "semantic-release pre && ./release-docker.sh && semantic-release post" + "semantic-release": "semantic-release pre && php release-docker.php && semantic-release post" }, "devDependencies": { "cz-conventional-changelog": "^2.0.0", diff --git a/release-docker.php b/release-docker.php new file mode 100755 index 0000000..d442fe5 --- /dev/null +++ b/release-docker.php @@ -0,0 +1,10 @@ +version; + +system("docker login -e=$dockerEmail -u=$dockerEmail -p=$dockerPassword"); +system("docker build -t felixfbecker/php-language-server:$version ."); +system("docker push felixfbecker/php-language-server:$version"); diff --git a/release-docker.sh b/release-docker.sh deleted file mode 100755 index 35e89d9..0000000 --- a/release-docker.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -docker build -t felixfbecker/php-language-server:${TRAVIS_TAG:1} . -docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" -docker push felixfbecker/php-language-server:${TRAVIS_TAG:1} From 99d8a361db205ee1179d541314b7e0c81d4d06f5 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 28 Oct 2017 14:24:36 -0700 Subject: [PATCH 19/55] build: fix typo in release-docker script --- release-docker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-docker.php b/release-docker.php index d442fe5..8cac2db 100755 --- a/release-docker.php +++ b/release-docker.php @@ -5,6 +5,6 @@ $dockerUsername = getenv('DOCKER_USERNAME'); $dockerPassword = getenv('DOCKER_PASSWORD'); $version = json_decode(file_get_contents(__DIR__ . '/package.json'))->version; -system("docker login -e=$dockerEmail -u=$dockerEmail -p=$dockerPassword"); +system("docker login -e=$dockerEmail -u=$dockerUsername -p=$dockerPassword"); system("docker build -t felixfbecker/php-language-server:$version ."); system("docker push felixfbecker/php-language-server:$version"); From c74076d84f4e865eb71208445231e5ab5c3e2e8f Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 29 Oct 2017 13:06:44 -0700 Subject: [PATCH 20/55] fix(cache): bump cache version (#508) the update of reflection-docblock means old caches are no longer valid. fixes #507 --- src/Indexer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Indexer.php b/src/Indexer.php index 9f8749d..2618d43 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -16,9 +16,9 @@ use function Sabre\Event\coroutine; class Indexer { /** - * @var The prefix for every cache item + * @var int The prefix for every cache item */ - const CACHE_VERSION = 1; + const CACHE_VERSION = 2; /** * @var FilesFinder From 7ae6452d1aaf44e96bb683ea7192a54576ad6cce Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 29 Oct 2017 17:45:06 -0700 Subject: [PATCH 21/55] refactor(index): rename isGlobal to isMember (#511) isGlobal was confusing because a non-member can be considered global vs namespaced --- src/CompletionProvider.php | 6 +++--- src/Definition.php | 5 +++-- src/DefinitionResolver.php | 2 +- .../cases/WithReturnTypehints.php.expected.json | 10 +++++----- ...sClassMembersShouldNotBeSymbols.php.expected.json | 2 +- .../arrayValueShouldBeBoolean.php.expected.json | 4 ++-- .../cases/caseStatement1.php.expected.json | 2 +- .../cases/classDefinition1.php.expected.json | 6 +++--- .../cases/classProperty1.php.expected.json | 8 ++++---- tests/Validation/cases/constants.php.expected.json | 6 +++--- tests/Validation/cases/constants2.php.expected.json | 6 +++--- tests/Validation/cases/constants3.php.expected.json | 6 +++--- tests/Validation/cases/constants4.php.expected.json | 6 +++--- tests/Validation/cases/constants5.php.expected.json | 6 +++--- ...constantsInFunctionParamDefault.php.expected.json | 4 ++-- .../docBlocksOnNamespaceDefinition.php.expected.json | 2 +- tests/Validation/cases/exceptions1.php.expected.json | 2 +- .../Validation/cases/ifStatement1.php.expected.json | 2 +- .../cases/interfaceProperty.php.expected.json | 2 +- .../magicConstantsShouldBeGlobal.php.expected.json | 2 +- tests/Validation/cases/magicConsts.php.expected.json | 4 ++-- .../Validation/cases/memberAccess1.php.expected.json | 6 +++--- .../Validation/cases/memberAccess2.php.expected.json | 6 +++--- .../Validation/cases/memberAccess3.php.expected.json | 6 +++--- .../Validation/cases/memberAccess4.php.expected.json | 6 +++--- .../Validation/cases/memberAccess5.php.expected.json | 6 +++--- tests/Validation/cases/memberCall1.php.expected.json | 6 +++--- .../cases/methodReturnType.php.expected.json | 4 ++-- .../cases/multipleNamespaces.php.expected.json | 12 ++++++------ .../multiplePreceedingComments.php.expected.json | 4 ++-- tests/Validation/cases/nameToken.php.expected.json | 4 ++-- tests/Validation/cases/namespaces2.php.expected.json | 2 +- tests/Validation/cases/namespaces5.php.expected.json | 2 +- tests/Validation/cases/namespaces6.php.expected.json | 2 +- tests/Validation/cases/namespaces8.php.expected.json | 2 +- .../cases/objectCreation.php.expected.json | 6 +++--- .../cases/objectCreation2.php.expected.json | 8 ++++---- .../cases/objectCreation3.php.expected.json | 4 ++-- tests/Validation/cases/param1.php.expected.json | 4 ++-- tests/Validation/cases/parent1.php.expected.json | 10 +++++----- tests/Validation/cases/parent3.php.expected.json | 10 +++++----- .../Validation/cases/propertyName1.php.expected.json | 4 ++-- .../Validation/cases/propertyName2.php.expected.json | 4 ++-- tests/Validation/cases/returnType.php.expected.json | 4 ++-- .../cases/scopedPropertyAccess.php.expected.json | 6 +++--- .../cases/scopedPropertyAccess2.php.expected.json | 2 +- .../cases/scopedPropertyAccess3.php.expected.json | 4 ++-- .../cases/scopedPropertyAccess5.php.expected.json | 4 ++-- tests/Validation/cases/self1.php.expected.json | 10 +++++----- tests/Validation/cases/self2.php.expected.json | 10 +++++----- tests/Validation/cases/self3.php.expected.json | 10 +++++----- tests/Validation/cases/self4.php.expected.json | 6 +++--- tests/Validation/cases/self5.php.expected.json | 6 +++--- tests/Validation/cases/static1.php.expected.json | 10 +++++----- tests/Validation/cases/static2.php.expected.json | 10 +++++----- tests/Validation/cases/static3.php.expected.json | 10 +++++----- tests/Validation/cases/static4.php.expected.json | 6 +++--- .../cases/staticMethodReturnType.php.expected.json | 6 +++--- .../cases/stringVariable.php.expected.json | 6 +++--- ...QualifiedNameOutsideOfNamespace.php.expected.json | 2 +- .../verifyFqsenOnClassProperty.php.expected.json | 6 +++--- 61 files changed, 165 insertions(+), 164 deletions(-) diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 2414ead..9a000e6 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -210,7 +210,7 @@ class CompletionProvider // Collect all definitions that match any of the prefixes foreach ($this->index->getDefinitions() as $fqn => $def) { foreach ($prefixes as $prefix) { - if (substr($fqn, 0, strlen($prefix)) === $prefix && !$def->isGlobal) { + if (substr($fqn, 0, strlen($prefix)) === $prefix && $def->isMember) { $list->items[] = CompletionItem::fromDefinition($def); } } @@ -243,7 +243,7 @@ class CompletionProvider // Collect all definitions that match any of the prefixes foreach ($this->index->getDefinitions() as $fqn => $def) { foreach ($prefixes as $prefix) { - if (substr(strtolower($fqn), 0, strlen($prefix)) === strtolower($prefix) && !$def->isGlobal) { + if (substr(strtolower($fqn), 0, strlen($prefix)) === strtolower($prefix) && $def->isMember) { $list->items[] = CompletionItem::fromDefinition($def); } } @@ -316,7 +316,7 @@ class CompletionProvider if ( // Exclude methods, properties etc. - $def->isGlobal + !$def->isMember && ( !$prefix || ( diff --git a/src/Definition.php b/src/Definition.php index 9a485c4..c27f871 100644 --- a/src/Definition.php +++ b/src/Definition.php @@ -39,12 +39,13 @@ class Definition public $extends; /** - * Only true for classes, interfaces, traits, functions and non-class constants + * False for classes, interfaces, traits, functions and non-class constants + * True for methods, properties and class constants * This is so methods and properties are not suggested in the global scope * * @var bool */ - public $isGlobal; + public $isMember; /** * True if this definition is affected by global namespace fallback (global function or global constant) diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index f6d82c5..93f961d 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -181,7 +181,7 @@ class DefinitionResolver ); // Interfaces, classes, traits, namespaces, functions, and global const elements - $def->isGlobal = ( + $def->isMember = !( $node instanceof PhpParser\ClassLike || ($node instanceof Node\Statement\NamespaceDefinition && $node->name !== null) || diff --git a/tests/Validation/cases/WithReturnTypehints.php.expected.json b/tests/Validation/cases/WithReturnTypehints.php.expected.json index 26b00e2..d11f271 100644 --- a/tests/Validation/cases/WithReturnTypehints.php.expected.json +++ b/tests/Validation/cases/WithReturnTypehints.php.expected.json @@ -20,7 +20,7 @@ "Fixtures\\Prophecy": { "fqn": "Fixtures\\Prophecy", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -41,7 +41,7 @@ "extends": [ "Fixtures\\Prophecy\\EmptyClass" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -60,7 +60,7 @@ "Fixtures\\Prophecy\\WithReturnTypehints->getSelf()": { "fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getSelf()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -80,7 +80,7 @@ "Fixtures\\Prophecy\\WithReturnTypehints->getName()": { "fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getName()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -100,7 +100,7 @@ "Fixtures\\Prophecy\\WithReturnTypehints->getParent()": { "fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getParent()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json b/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json index b0625d7..51343f1 100644 --- a/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json +++ b/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json @@ -4,7 +4,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json index d58986d..107877e 100644 --- a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json +++ b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json @@ -4,7 +4,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -23,7 +23,7 @@ "A->foo": { "fqn": "A->foo", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/caseStatement1.php.expected.json b/tests/Validation/cases/caseStatement1.php.expected.json index 573dd17..9746749 100644 --- a/tests/Validation/cases/caseStatement1.php.expected.json +++ b/tests/Validation/cases/caseStatement1.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/classDefinition1.php.expected.json b/tests/Validation/cases/classDefinition1.php.expected.json index 6c418b7..670b5de 100644 --- a/tests/Validation/cases/classDefinition1.php.expected.json +++ b/tests/Validation/cases/classDefinition1.php.expected.json @@ -11,7 +11,7 @@ "TestNamespace": { "fqn": "TestNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "TestNamespace\\A": { "fqn": "TestNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "TestNamespace\\A->a": { "fqn": "TestNamespace\\A->a", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/classProperty1.php.expected.json b/tests/Validation/cases/classProperty1.php.expected.json index e00107e..921bf0b 100644 --- a/tests/Validation/cases/classProperty1.php.expected.json +++ b/tests/Validation/cases/classProperty1.php.expected.json @@ -11,7 +11,7 @@ "TestNamespace": { "fqn": "TestNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "TestNamespace\\TestClass": { "fqn": "TestNamespace\\TestClass", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "TestNamespace\\TestClass->testProperty": { "fqn": "TestNamespace\\TestClass->testProperty", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -69,7 +69,7 @@ "TestNamespace\\TestClass->testMethod()": { "fqn": "TestNamespace\\TestClass->testMethod()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/constants.php.expected.json b/tests/Validation/cases/constants.php.expected.json index d629870..a76c059 100644 --- a/tests/Validation/cases/constants.php.expected.json +++ b/tests/Validation/cases/constants.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A::suite()": { "fqn": "MyNamespace\\A::suite()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/constants2.php.expected.json b/tests/Validation/cases/constants2.php.expected.json index 66c678f..ae5a2ce 100644 --- a/tests/Validation/cases/constants2.php.expected.json +++ b/tests/Validation/cases/constants2.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A::suite()": { "fqn": "MyNamespace\\A::suite()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/constants3.php.expected.json b/tests/Validation/cases/constants3.php.expected.json index 03f00ba..c6ad922 100644 --- a/tests/Validation/cases/constants3.php.expected.json +++ b/tests/Validation/cases/constants3.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A::suite()": { "fqn": "MyNamespace\\A::suite()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/constants4.php.expected.json b/tests/Validation/cases/constants4.php.expected.json index 62ce430..bc46cf1 100644 --- a/tests/Validation/cases/constants4.php.expected.json +++ b/tests/Validation/cases/constants4.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A->suite()": { "fqn": "MyNamespace\\A->suite()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/constants5.php.expected.json b/tests/Validation/cases/constants5.php.expected.json index bb441c8..6bd7b8e 100644 --- a/tests/Validation/cases/constants5.php.expected.json +++ b/tests/Validation/cases/constants5.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -27,7 +27,7 @@ "MyNamespace\\Mbstring": { "fqn": "MyNamespace\\Mbstring", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -46,7 +46,7 @@ "MyNamespace\\Mbstring::MB_CASE_FOLD": { "fqn": "MyNamespace\\Mbstring::MB_CASE_FOLD", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json b/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json index 8f9a212..b49f5a9 100644 --- a/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json +++ b/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json @@ -8,7 +8,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -27,7 +27,7 @@ "A->b()": { "fqn": "A->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json b/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json index 73f6bee..dd1737a 100644 --- a/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json +++ b/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json @@ -4,7 +4,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/exceptions1.php.expected.json b/tests/Validation/cases/exceptions1.php.expected.json index a4a71d1..c1ee1bd 100644 --- a/tests/Validation/cases/exceptions1.php.expected.json +++ b/tests/Validation/cases/exceptions1.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/ifStatement1.php.expected.json b/tests/Validation/cases/ifStatement1.php.expected.json index 18efe9f..98e6572 100644 --- a/tests/Validation/cases/ifStatement1.php.expected.json +++ b/tests/Validation/cases/ifStatement1.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/interfaceProperty.php.expected.json b/tests/Validation/cases/interfaceProperty.php.expected.json index 178834d..10c49f0 100644 --- a/tests/Validation/cases/interfaceProperty.php.expected.json +++ b/tests/Validation/cases/interfaceProperty.php.expected.json @@ -4,7 +4,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json b/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json index f62d214..a9c2162 100644 --- a/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json +++ b/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json @@ -11,7 +11,7 @@ "B": { "fqn": "B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/magicConsts.php.expected.json b/tests/Validation/cases/magicConsts.php.expected.json index a37791b..6f863b9 100644 --- a/tests/Validation/cases/magicConsts.php.expected.json +++ b/tests/Validation/cases/magicConsts.php.expected.json @@ -8,7 +8,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -27,7 +27,7 @@ "A::$deprecationsTriggered": { "fqn": "A::$deprecationsTriggered", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/memberAccess1.php.expected.json b/tests/Validation/cases/memberAccess1.php.expected.json index 7f9630a..c039e5e 100644 --- a/tests/Validation/cases/memberAccess1.php.expected.json +++ b/tests/Validation/cases/memberAccess1.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A::a()": { "fqn": "MyNamespace\\A::a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/memberAccess2.php.expected.json b/tests/Validation/cases/memberAccess2.php.expected.json index 7b3ce1d..50902a1 100644 --- a/tests/Validation/cases/memberAccess2.php.expected.json +++ b/tests/Validation/cases/memberAccess2.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A::a()": { "fqn": "MyNamespace\\A::a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/memberAccess3.php.expected.json b/tests/Validation/cases/memberAccess3.php.expected.json index 520dae8..d91b27d 100644 --- a/tests/Validation/cases/memberAccess3.php.expected.json +++ b/tests/Validation/cases/memberAccess3.php.expected.json @@ -26,7 +26,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -45,7 +45,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -64,7 +64,7 @@ "MyNamespace\\A::getInitializer()": { "fqn": "MyNamespace\\A::getInitializer()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/memberAccess4.php.expected.json b/tests/Validation/cases/memberAccess4.php.expected.json index 1d51b85..0b7e1bf 100644 --- a/tests/Validation/cases/memberAccess4.php.expected.json +++ b/tests/Validation/cases/memberAccess4.php.expected.json @@ -17,7 +17,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -36,7 +36,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -55,7 +55,7 @@ "MyNamespace\\A->testRequest()": { "fqn": "MyNamespace\\A->testRequest()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/memberAccess5.php.expected.json b/tests/Validation/cases/memberAccess5.php.expected.json index 57aca0a..050cf3b 100644 --- a/tests/Validation/cases/memberAccess5.php.expected.json +++ b/tests/Validation/cases/memberAccess5.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -27,7 +27,7 @@ "MyNamespace\\ParseErrorsTest": { "fqn": "MyNamespace\\ParseErrorsTest", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -46,7 +46,7 @@ "MyNamespace\\ParseErrorsTest->setUp()": { "fqn": "MyNamespace\\ParseErrorsTest->setUp()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/memberCall1.php.expected.json b/tests/Validation/cases/memberCall1.php.expected.json index 416a705..d02d7e3 100644 --- a/tests/Validation/cases/memberCall1.php.expected.json +++ b/tests/Validation/cases/memberCall1.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\ParseErrorsTest": { "fqn": "MyNamespace\\ParseErrorsTest", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\ParseErrorsTest->setAccount()": { "fqn": "MyNamespace\\ParseErrorsTest->setAccount()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/methodReturnType.php.expected.json b/tests/Validation/cases/methodReturnType.php.expected.json index 54d79d6..2c89994 100644 --- a/tests/Validation/cases/methodReturnType.php.expected.json +++ b/tests/Validation/cases/methodReturnType.php.expected.json @@ -8,7 +8,7 @@ "FooClass": { "fqn": "FooClass", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -27,7 +27,7 @@ "FooClass->foo()": { "fqn": "FooClass->foo()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/multipleNamespaces.php.expected.json b/tests/Validation/cases/multipleNamespaces.php.expected.json index 30fe596..fa51f2d 100644 --- a/tests/Validation/cases/multipleNamespaces.php.expected.json +++ b/tests/Validation/cases/multipleNamespaces.php.expected.json @@ -17,7 +17,7 @@ "MyNamespace1": { "fqn": "MyNamespace1", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -36,7 +36,7 @@ "MyNamespace1\\B": { "fqn": "MyNamespace1\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -55,7 +55,7 @@ "MyNamespace1\\B->b()": { "fqn": "MyNamespace1\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -75,7 +75,7 @@ "MyNamespace2": { "fqn": "MyNamespace2", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -96,7 +96,7 @@ "extends": [ "MyNamespace2\\MyNamespace1\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -115,7 +115,7 @@ "MyNamespace2\\A->a()": { "fqn": "MyNamespace2\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/multiplePreceedingComments.php.expected.json b/tests/Validation/cases/multiplePreceedingComments.php.expected.json index 5ca3dd7..96cbb4a 100644 --- a/tests/Validation/cases/multiplePreceedingComments.php.expected.json +++ b/tests/Validation/cases/multiplePreceedingComments.php.expected.json @@ -4,7 +4,7 @@ "Foo": { "fqn": "Foo", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -23,7 +23,7 @@ "Foo->fn()": { "fqn": "Foo->fn()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/nameToken.php.expected.json b/tests/Validation/cases/nameToken.php.expected.json index 0ecd53f..c06217b 100644 --- a/tests/Validation/cases/nameToken.php.expected.json +++ b/tests/Validation/cases/nameToken.php.expected.json @@ -4,7 +4,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -23,7 +23,7 @@ "A->b()": { "fqn": "A->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/namespaces2.php.expected.json b/tests/Validation/cases/namespaces2.php.expected.json index 42243e5..0dffc9f 100644 --- a/tests/Validation/cases/namespaces2.php.expected.json +++ b/tests/Validation/cases/namespaces2.php.expected.json @@ -17,7 +17,7 @@ "MyNamespace1": { "fqn": "MyNamespace1", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/namespaces5.php.expected.json b/tests/Validation/cases/namespaces5.php.expected.json index 5ffe02d..e609ca2 100644 --- a/tests/Validation/cases/namespaces5.php.expected.json +++ b/tests/Validation/cases/namespaces5.php.expected.json @@ -26,7 +26,7 @@ "B": { "fqn": "B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/namespaces6.php.expected.json b/tests/Validation/cases/namespaces6.php.expected.json index 1657f28..bf5a045 100644 --- a/tests/Validation/cases/namespaces6.php.expected.json +++ b/tests/Validation/cases/namespaces6.php.expected.json @@ -4,7 +4,7 @@ "A\\B": { "fqn": "A\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/namespaces8.php.expected.json b/tests/Validation/cases/namespaces8.php.expected.json index 71017d9..d303647 100644 --- a/tests/Validation/cases/namespaces8.php.expected.json +++ b/tests/Validation/cases/namespaces8.php.expected.json @@ -14,7 +14,7 @@ "LanguageServer\\Tests\\Utils": { "fqn": "LanguageServer\\Tests\\Utils", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/objectCreation.php.expected.json b/tests/Validation/cases/objectCreation.php.expected.json index a8fce0f..8fec38f 100644 --- a/tests/Validation/cases/objectCreation.php.expected.json +++ b/tests/Validation/cases/objectCreation.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -27,7 +27,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -46,7 +46,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/objectCreation2.php.expected.json b/tests/Validation/cases/objectCreation2.php.expected.json index 2ec8314..e546528 100644 --- a/tests/Validation/cases/objectCreation2.php.expected.json +++ b/tests/Validation/cases/objectCreation2.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -68,7 +68,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/objectCreation3.php.expected.json b/tests/Validation/cases/objectCreation3.php.expected.json index f0cc31a..d2d3e2f 100644 --- a/tests/Validation/cases/objectCreation3.php.expected.json +++ b/tests/Validation/cases/objectCreation3.php.expected.json @@ -8,7 +8,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -27,7 +27,7 @@ "A->a()": { "fqn": "A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/param1.php.expected.json b/tests/Validation/cases/param1.php.expected.json index 33c99db..40cfbd1 100644 --- a/tests/Validation/cases/param1.php.expected.json +++ b/tests/Validation/cases/param1.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -27,7 +27,7 @@ "MyNamespace\\init()": { "fqn": "MyNamespace\\init()", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/parent1.php.expected.json b/tests/Validation/cases/parent1.php.expected.json index eab232e..8b29461 100644 --- a/tests/Validation/cases/parent1.php.expected.json +++ b/tests/Validation/cases/parent1.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -71,7 +71,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -90,7 +90,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/parent3.php.expected.json b/tests/Validation/cases/parent3.php.expected.json index aedb4b2..1e51b84 100644 --- a/tests/Validation/cases/parent3.php.expected.json +++ b/tests/Validation/cases/parent3.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/propertyName1.php.expected.json b/tests/Validation/cases/propertyName1.php.expected.json index 42b2ee9..cc31ed6 100644 --- a/tests/Validation/cases/propertyName1.php.expected.json +++ b/tests/Validation/cases/propertyName1.php.expected.json @@ -4,7 +4,7 @@ "MyClass": { "fqn": "MyClass", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -23,7 +23,7 @@ "MyClass->mainPropertyName": { "fqn": "MyClass->mainPropertyName", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/propertyName2.php.expected.json b/tests/Validation/cases/propertyName2.php.expected.json index 5a5c914..d4efb42 100644 --- a/tests/Validation/cases/propertyName2.php.expected.json +++ b/tests/Validation/cases/propertyName2.php.expected.json @@ -4,7 +4,7 @@ "MyClass": { "fqn": "MyClass", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -23,7 +23,7 @@ "MyClass->mainPropertyName": { "fqn": "MyClass->mainPropertyName", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/returnType.php.expected.json b/tests/Validation/cases/returnType.php.expected.json index cf9cc63..20cdadf 100644 --- a/tests/Validation/cases/returnType.php.expected.json +++ b/tests/Validation/cases/returnType.php.expected.json @@ -11,7 +11,7 @@ "TestNamespace": { "fqn": "TestNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "TestNamespace\\whatever()": { "fqn": "TestNamespace\\whatever()", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/scopedPropertyAccess.php.expected.json b/tests/Validation/cases/scopedPropertyAccess.php.expected.json index 52b6e7a..ec50c7a 100644 --- a/tests/Validation/cases/scopedPropertyAccess.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A::a()": { "fqn": "MyNamespace\\A::a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/scopedPropertyAccess2.php.expected.json b/tests/Validation/cases/scopedPropertyAccess2.php.expected.json index e5f6850..e1712cd 100644 --- a/tests/Validation/cases/scopedPropertyAccess2.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess2.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/scopedPropertyAccess3.php.expected.json b/tests/Validation/cases/scopedPropertyAccess3.php.expected.json index aa508bc..913721b 100644 --- a/tests/Validation/cases/scopedPropertyAccess3.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess3.php.expected.json @@ -11,7 +11,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -30,7 +30,7 @@ "A::$a": { "fqn": "A::$a", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/scopedPropertyAccess5.php.expected.json b/tests/Validation/cases/scopedPropertyAccess5.php.expected.json index bd4ee70..7e56123 100644 --- a/tests/Validation/cases/scopedPropertyAccess5.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess5.php.expected.json @@ -17,7 +17,7 @@ "TestClass": { "fqn": "TestClass", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -36,7 +36,7 @@ "TestClass::$testProperty": { "fqn": "TestClass::$testProperty", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/self1.php.expected.json b/tests/Validation/cases/self1.php.expected.json index eb37fc7..2ce8e43 100644 --- a/tests/Validation/cases/self1.php.expected.json +++ b/tests/Validation/cases/self1.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/self2.php.expected.json b/tests/Validation/cases/self2.php.expected.json index 2280b1a..cc00e3a 100644 --- a/tests/Validation/cases/self2.php.expected.json +++ b/tests/Validation/cases/self2.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/self3.php.expected.json b/tests/Validation/cases/self3.php.expected.json index 3f69299..9f25ba1 100644 --- a/tests/Validation/cases/self3.php.expected.json +++ b/tests/Validation/cases/self3.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/self4.php.expected.json b/tests/Validation/cases/self4.php.expected.json index ec3ae07..7f7b25e 100644 --- a/tests/Validation/cases/self4.php.expected.json +++ b/tests/Validation/cases/self4.php.expected.json @@ -23,7 +23,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -42,7 +42,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -61,7 +61,7 @@ "MyNamespace\\A::suite()": { "fqn": "MyNamespace\\A::suite()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/self5.php.expected.json b/tests/Validation/cases/self5.php.expected.json index 6727689..eb41832 100644 --- a/tests/Validation/cases/self5.php.expected.json +++ b/tests/Validation/cases/self5.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -27,7 +27,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -46,7 +46,7 @@ "MyNamespace\\A->typesProvider()": { "fqn": "MyNamespace\\A->typesProvider()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/static1.php.expected.json b/tests/Validation/cases/static1.php.expected.json index 69f8de0..453edc8 100644 --- a/tests/Validation/cases/static1.php.expected.json +++ b/tests/Validation/cases/static1.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/static2.php.expected.json b/tests/Validation/cases/static2.php.expected.json index 17f9a66..87e8e9a 100644 --- a/tests/Validation/cases/static2.php.expected.json +++ b/tests/Validation/cases/static2.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/static3.php.expected.json b/tests/Validation/cases/static3.php.expected.json index f6e5189..37524ea 100644 --- a/tests/Validation/cases/static3.php.expected.json +++ b/tests/Validation/cases/static3.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/static4.php.expected.json b/tests/Validation/cases/static4.php.expected.json index 3d05ede..4bcdd9d 100644 --- a/tests/Validation/cases/static4.php.expected.json +++ b/tests/Validation/cases/static4.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -29,7 +29,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -48,7 +48,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/staticMethodReturnType.php.expected.json b/tests/Validation/cases/staticMethodReturnType.php.expected.json index c178f0a..e6661ec 100644 --- a/tests/Validation/cases/staticMethodReturnType.php.expected.json +++ b/tests/Validation/cases/staticMethodReturnType.php.expected.json @@ -8,7 +8,7 @@ "FooClass": { "fqn": "FooClass", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -27,7 +27,7 @@ "FooClass::staticFoo()": { "fqn": "FooClass::staticFoo()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, @@ -47,7 +47,7 @@ "FooClass->bar()": { "fqn": "FooClass->bar()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/stringVariable.php.expected.json b/tests/Validation/cases/stringVariable.php.expected.json index fed8a23..1d4c7e3 100644 --- a/tests/Validation/cases/stringVariable.php.expected.json +++ b/tests/Validation/cases/stringVariable.php.expected.json @@ -8,7 +8,7 @@ "B": { "fqn": "B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -27,7 +27,7 @@ "B->hi": { "fqn": "B->hi", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -47,7 +47,7 @@ "B->a()": { "fqn": "B->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json b/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json index f686093..509907c 100644 --- a/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json +++ b/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json @@ -8,7 +8,7 @@ "SomeNamespace": { "fqn": "SomeNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json b/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json index f6851bf..662a7ed 100644 --- a/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json +++ b/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json @@ -11,7 +11,7 @@ "Foo": { "fqn": "Foo", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -30,7 +30,7 @@ "Foo->bar": { "fqn": "Foo->bar", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -50,7 +50,7 @@ "Foo->foo()": { "fqn": "Foo->foo()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, From 744062c14e3d104a3159cb969881baf9bb4dfc9e Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 29 Oct 2017 17:53:37 -0700 Subject: [PATCH 22/55] ci: add AppVeyor to test Windows closes #40 --- README.md | 3 +- appveyor.yml | 51 +++++++++++++++++++++++++++++ tests/Validation/ValidationTest.php | 5 ++- 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 appveyor.yml diff --git a/README.md b/README.md index 0aad4e5..017ba72 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # PHP Language Server [![Version](https://img.shields.io/packagist/v/felixfbecker/language-server.svg)](https://packagist.org/packages/felixfbecker/language-server) -[![Build Status](https://travis-ci.org/felixfbecker/php-language-server.svg?branch=master)](https://travis-ci.org/felixfbecker/php-language-server) +[![Linux Build Status](https://travis-ci.org/felixfbecker/php-language-server.svg?branch=master)](https://travis-ci.org/felixfbecker/php-language-server) +[![Windows Build status](https://ci.appveyor.com/api/projects/status/2sp5ll052wdjqmdm/branch/master?svg=true)](https://ci.appveyor.com/project/felixfbecker/php-language-server/branch/master) [![Coverage](https://codecov.io/gh/felixfbecker/php-language-server/branch/master/graph/badge.svg)](https://codecov.io/gh/felixfbecker/php-language-server) [![Dependency Status](https://gemnasium.com/badges/github.com/felixfbecker/php-language-server.svg)](https://gemnasium.com/github.com/felixfbecker/php-language-server) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..13dfd74 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,51 @@ +version: '{build}' + +platform: + - x64 + +skip_tags: true +skip_branch_with_pr: true +clone_depth: 1 +max_jobs: 3 + +cache: + - '%LOCALAPPDATA%\Composer' + +environment: + ANSICON: 121x90 (121x90) + matrix: + - { PHP_VERSION: '7.1.11', VC_VERSION: '14', XDEBUG_VERSION: '2.5.5' } + +install: + # Enable Windows Update service, needed to install vcredist2015 (dependency of php) + - ps: Set-Service wuauserv -StartupType Manual + - choco install -y php --version %PHP_VERSION% + - choco install -y composer + - refreshenv + - composer install --no-interaction --no-progress --prefer-dist + # Install XDebug for code coverage + - ps: | + $client = New-Object System.Net.WebClient + $phpMinorVersion = $env:PHP_VERSION -replace '\.\d+$' + $xdebugUrl = "https://xdebug.org/files/php_xdebug-$env:XDEBUG_VERSION-$phpMinorVersion-vc14-nts-x86_64.dll" + $phpDir = (Get-Item (Get-Command php).Source).Directory.FullName + $xdebugPath = Join-Path $phpDir ext\xdebug.dll + $client.DownloadFile($xdebugUrl, $xdebugPath) + $phpIniPath = Join-Path $phpDir php.ini + Add-Content $phpIniPath @" + zend_extension=$xdebugPath + "@ + +build: off + +test_script: + - vendor\bin\phpunit --coverage-clover=coverage/coverage.xml + +after_test: + - ps: | + # Delete vendor because it causes problems with codecovs report search + # https://github.com/codecov/codecov-bash/issues/96 + Remove-Item -Recurse -Force vendor + $env:PATH = 'C:\msys64\usr\bin;' + $env:PATH + Invoke-WebRequest -Uri 'https://codecov.io/bash' -OutFile codecov.sh + bash codecov.sh -f 'coverage/coverage.xml' diff --git a/tests/Validation/ValidationTest.php b/tests/Validation/ValidationTest.php index 3fc7429..9057c9d 100644 --- a/tests/Validation/ValidationTest.php +++ b/tests/Validation/ValidationTest.php @@ -96,7 +96,9 @@ class ValidationTest extends TestCase $testCasesDir = realpath(__DIR__ . '/cases'); foreach ($refsAndDefs['references'] as $key => $list) { $fixedPathRefs = array_map(function ($ref) use ($testCasesDir) { - return str_replace($testCasesDir, '.', $ref); + $ref = str_replace($testCasesDir, '.', $ref); + $ref = str_replace(DIRECTORY_SEPARATOR, '/', $ref); + return $ref; }, $list); $refsAndDefs['references']->$key = $fixedPathRefs; @@ -107,6 +109,7 @@ class ValidationTest extends TestCase if ($def !== null && $def->symbolInformation !== null && $def->symbolInformation->location !== null && $def->symbolInformation->location->uri !== null) { $def->symbolInformation->location->uri = str_replace($testCasesDir, '.', $def->symbolInformation->location->uri); + $def->symbolInformation->location->uri = str_replace(DIRECTORY_SEPARATOR, '/', $def->symbolInformation->location->uri); } } From 1edbe35609c50dcd7d51219b07622f6e527b752b Mon Sep 17 00:00:00 2001 From: Jens Hausdorf Date: Mon, 30 Oct 2017 11:33:19 +0100 Subject: [PATCH 23/55] refactor: use FunctionLike Interface (#505) --- src/CompletionProvider.php | 4 ++-- src/DefinitionResolver.php | 4 ++-- src/ParserHelpers.php | 10 +--------- src/TreeAnalyzer.php | 4 ++-- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 9a000e6..68c5a0c 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -415,7 +415,7 @@ class CompletionProvider // Walk the AST upwards until a scope boundary is met $level = $node; - while ($level && !ParserHelpers\isFunctionLike($level)) { + while ($level && !($level instanceof PhpParser\FunctionLike)) { // Walk siblings before the node $sibling = $level; while ($sibling = $sibling->getPreviousSibling()) { @@ -429,7 +429,7 @@ class CompletionProvider // If the traversal ended because a function was met, // also add its parameters and closure uses to the result list - if ($level && ParserHelpers\isFunctionLike($level) && $level->parameters !== null) { + if ($level && $level instanceof PhpParser\FunctionLike && $level->parameters !== null) { foreach ($level->parameters->getValues() as $param) { $paramName = $param->getName(); if (empty($namePrefix) || strpos($paramName, $namePrefix) !== false) { diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 93f961d..620527e 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -509,7 +509,7 @@ class DefinitionResolver // Traverse the AST up do { // If a function is met, check the parameters and use statements - if (ParserHelpers\isFunctionLike($n)) { + if ($n instanceof PhpParser\FunctionLike) { if ($n->parameters !== null) { foreach ($n->parameters->getElements() as $param) { if ($param->getName() === $name) { @@ -1018,7 +1018,7 @@ class DefinitionResolver // 1. doc block // 2. return type hint // 3. TODO: infer from return statements - if (ParserHelpers\isFunctionLike($node)) { + if ($node instanceof PhpParser\FunctionLike) { // Functions/methods $docBlock = $this->getDocBlock($node); if ( diff --git a/src/ParserHelpers.php b/src/ParserHelpers.php index 0ea210a..f20fb1f 100644 --- a/src/ParserHelpers.php +++ b/src/ParserHelpers.php @@ -25,7 +25,7 @@ function isConstantFetch(Node $node) : bool $parent instanceof Node\Expression\CallExpression || $parent instanceof Node\Expression\ObjectCreationExpression || $parent instanceof Node\Expression\ScopedPropertyAccessExpression || - isFunctionLike($parent) || + $parent instanceof PhpParser\FunctionLike || ( $parent instanceof Node\Expression\BinaryExpression && $parent->operator->kind === PhpParser\TokenKind::InstanceOfKeyword @@ -38,14 +38,6 @@ function getFunctionLikeDeclarationFromParameter(Node\Parameter $node) return $node->parent->parent; } -function isFunctionLike(Node $node) -{ - return - $node instanceof Node\Statement\FunctionDeclaration || - $node instanceof Node\MethodDeclaration || - $node instanceof Node\Expression\AnonymousFunctionCreationExpression; -} - function isBooleanExpression($expression) : bool { if (!($expression instanceof Node\Expression\BinaryExpression)) { diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index 81c96e1..58de8db 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -72,10 +72,10 @@ class TreeAnalyzer $range = PhpParser\PositionUtilities::getRangeFromPosition($error->start, $error->length, $this->sourceFileNode->fileContents); switch ($error->kind) { - case \Microsoft\PhpParser\DiagnosticKind::Error: + case PhpParser\DiagnosticKind::Error: $severity = DiagnosticSeverity::ERROR; break; - case \Microsoft\PhpParser\DiagnosticKind::Warning: + case PhpParser\DiagnosticKind::Warning: default: $severity = DiagnosticSeverity::WARNING; break; From d3c9133892022a7e8aa2711d487546cbde5ab253 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Mon, 30 Oct 2017 21:12:44 -0700 Subject: [PATCH 24/55] ci(appveyor): cache chocolatey downloads --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 13dfd74..fa6d26a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,6 +10,7 @@ max_jobs: 3 cache: - '%LOCALAPPDATA%\Composer' + - '%LOCALAPPDATA%\Temp\Chocolatey' environment: ANSICON: 121x90 (121x90) @@ -19,6 +20,7 @@ environment: install: # Enable Windows Update service, needed to install vcredist2015 (dependency of php) - ps: Set-Service wuauserv -StartupType Manual + - choco config set cacheLocation %LOCALAPPDATA%\Temp\Chocolatey - choco install -y php --version %PHP_VERSION% - choco install -y composer - refreshenv From ac6bce929fa1a4265d92628b13153c0361bf67ef Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Mon, 30 Oct 2017 22:51:23 -0700 Subject: [PATCH 25/55] chore: get patch versions of tolerant-php-parser --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0504c00..ceb357d 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "composer/composer": "^1.3", "felixfbecker/advanced-json-rpc": "^3.0.0", "jetbrains/phpstorm-stubs": "dev-master", - "microsoft/tolerant-php-parser": "^0.0.6", + "microsoft/tolerant-php-parser": "0.0.*", "netresearch/jsonmapper": "^1.0", "phpdocumentor/reflection-docblock": "^4.0.0", "sabre/event": "^5.0", From 6dbeef63bc12c3e0b5b1f8f384c1ac82c3a6e17e Mon Sep 17 00:00:00 2001 From: Nate Eagleson Date: Wed, 1 Nov 2017 12:38:54 -0400 Subject: [PATCH 26/55] docs: correct parse-stubs section in readme (#502) As the parse-stubs step is done automatically by `composer install` since https://github.com/felixfbecker/php-language-server/commit/34d3d2030dabc29bd919d0ea795bcb8d994228de, we no longer need to explicitly instruct people to do it. Note that sometimes you must parse the PHP stubs manually --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 017ba72..90c66cb 100644 --- a/README.md +++ b/README.md @@ -192,9 +192,6 @@ Clone the repository and run composer install to install dependencies. -Then parse the stubs with - - composer run-script parse-stubs Run the tests with @@ -203,3 +200,7 @@ Run the tests with Lint with composer lint + +The project parses PHPStorm's PHP stubs to get support for PHP builtins. It re-parses them as needed after Composer processes, but after some code changes (such as ones involving the index or parsing) you may have to explicitly re-parse them: + + composer run-script parse-stubs From e9fc97d430e691485d7ff995cb05b9659aead362 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 1 Nov 2017 23:39:38 -0700 Subject: [PATCH 27/55] chore: extend export-ignore file list --- .gitattributes | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitattributes b/.gitattributes index 7600d2f..688e780 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,18 +2,22 @@ /.vscode export-ignore /fixtures export-ignore -/tests export-ignore /images export-ignore +/tests export-ignore /validation export-ignore +/.dockerignore export-ignore +/.editorconfig export-ignore /.gitattributes export-ignore /.gitignore export-ignore /.gitmodules export-ignore -/.dockerignore export-ignore +/.npmrc export-ignore /.travis.yml export-ignore /codecov.yml export-ignore +/dependencies.yml export-ignore /Dockerfile export-ignore +/package.json export-ignore /Performance.php export-ignore /php.ini export-ignore /phpcs.xml.dist export-ignore /phpunit.xml.dist export-ignore -/README.md export-ignore +/release-docker.php export-ignore From f00fd1b62c7f2928b92a08a9c258767480c7344b Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 4 Nov 2017 23:57:51 -0700 Subject: [PATCH 28/55] fix(formatting): drop PHP CodeSniffer (#504) At this point there are countless issues about the formatting done by CodeSniffer. It plain out doesn't work in many cases, overrides format options that are contributed by other extensions in VS Code and does not reuse any of our AST parsing. For that reason, I am starting to think there is no reason to keep it in here until we have proper pretty-printing support from https://github.com/Microsoft/tolerant-php-parser that actually reuses our ASTs and can work while editing. For people who want to use CodeSniffer to format their code, there could be a standalone CodeSniffer language server (like there is a TSLint language server and ESLint language server). As said, we don't reuse our state anyway. BREAKING CHANGE: removes formatting support closes #501 closes #474 closes #473 closes #468 closes #450 closes #445 closes #443 closes #423 closes #343 closes #296 closes #293 closes #499 closes #471 --- README.md | 3 - composer.json | 4 +- fixtures/format.php | 20 ---- fixtures/format_expected.php | 19 ---- images/formatDocument.gif | Bin 52182 -> 0 bytes src/Formatter.php | 107 ------------------- src/LanguageServer.php | 2 - src/PhpDocument.php | 13 --- src/Server/TextDocument.php | 14 --- tests/FormatterTest.php | 28 ----- tests/LanguageServerTest.php | 1 - tests/Server/TextDocument/FormattingTest.php | 50 --------- 12 files changed, 2 insertions(+), 259 deletions(-) delete mode 100644 fixtures/format.php delete mode 100644 fixtures/format_expected.php delete mode 100644 images/formatDocument.gif delete mode 100644 src/Formatter.php delete mode 100644 tests/FormatterTest.php delete mode 100644 tests/Server/TextDocument/FormattingTest.php diff --git a/README.md b/README.md index 90c66cb..8e7117c 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,6 @@ For Parameters, it will return the `@param` tag. The query is matched case-insensitively against the fully qualified name of the symbol. Non-Standard: An empty query will return _all_ symbols found in the workspace. -### [Document Formatting](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#document-formatting-request) -![Document Formatting demo](images/formatDocument.gif) - ### Error reporting through [Publish Diagnostics](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#publishdiagnostics-notification) ![Error reporting demo](images/publishDiagnostics.png) diff --git a/composer.json b/composer.json index ceb357d..a21535b 100644 --- a/composer.json +++ b/composer.json @@ -30,12 +30,12 @@ "phpdocumentor/reflection-docblock": "^4.0.0", "sabre/event": "^5.0", "sabre/uri": "^2.0", - "squizlabs/php_codesniffer": "3.0.0RC3", "webmozart/glob": "^4.1", "webmozart/path-util": "^2.3" }, "require-dev": { - "phpunit/phpunit": "^6.3" + "phpunit/phpunit": "^6.3", + "squizlabs/php_codesniffer": "^3.1" }, "autoload": { "psr-4": { diff --git a/fixtures/format.php b/fixtures/format.php deleted file mode 100644 index bd35640..0000000 --- a/fixtures/format.php +++ /dev/null @@ -1,20 +0,0 @@ -@&W9+-{7-kJA9ZeZoTYkbbf)fD1 z&Wz)!BO=%lfw5wbFhGPVBhZS7Ks7FRU8J?HfVrlSfrfytI#N@M_txzjl6st?x}1Vq zTmp*Re8Rj)BrorOCFK8%e+CF3dBl+1ioBdU-0ZrXaD5K=Z4S5=CtQUSF3$y*<%Uc1 zz@>QMQhabJez>>*TvQM)EC3hcgA4J(g}C8DoNz&QxFDSEKLqE2!@1#bPB@$c{!g&5 zurM?I6W8~wEG%$1+kXho{&&E?RD>_|3t0XT;koKpeLr32?Qha;@v2xmCL z3(obNo!5h%&x!k{C9jl;fV`1_lCiLwshF0ToPmY1={*heyE=C*bS>_fSr}SeKU(Nn zSQy;BXL9Gk9dkFgANX}8t%$F%Gma8sS zY$?_3EWO=Ts@M6^pt;JVpx!jS#VozUG=0D5DAJZ@n(Jkd9NLik?>iibQ?jeV-0^z>fFQ2PKfj^4u(qb4x}~6^J+HjGsGz4ftGgt#yCk)b|lzj)NII5h9twEnvP`P8cG+`8w&uK&_$^JgZqXU8)+{*W4In3<}n7-@(J zix3h30053N5C{q=0tj9|2mDhe003xk*i(=KO%I$xHG%dF5ugV*ICt^SFUZ*;LI9=x1)?$Bk!RQC&P$=8&n!@oM z?Kk`n2WpBY3ygB`eGc03?@7o?kWpNpN@hRWbVjkI{~}3|L|Q8LA0p$?VfPynGj!|9 zzc%@HN^N@Olip9cOFh1sCF1_A6GJ0lHFWBbL?oYkDcOHK*sg{0ru{ZwU%ftxRTEEs zUz^LL=Iy}P<)t0>x+tVgfm0na;#&5UhmO-s>-TFdPkts&&_>zDO5Ym+YlAlu{k%!g zFTzfdK2L+qh+A}2KMhfqg|6cFao&))wlB|l^!&`U#DAUauMB?pGuF_@7f%!`>%=Fn zQtS>n>f(BKgnjF}KHlE>6m_ckmheaexrC+-a%9&w9^xTVy=sWa;DAACbsWQQ)Z@6I zQ15PzZ{{1z(IP#J(_x6E0`^yU@*|vb!c?VNY!bar#Z$QiauQmGSyz5dGS8=^Z^^|rLq;P zN%H3U2NV%MjMlEywlIqX#%J;f^kaR^tn*$3U51zE8Oswi=uq=Rh|kt$0X9)RPwhdt z%T`fgKJ$1X`P<^H5`0AuGk*zL1OIke{aW#MdGqns_Qy6Nft`vjde@!GKAw`Dsv)WE zo$4`7f!&&^JFdH*<{V3QYZraCck6H_EZvOZ{6=c^8~H3{_c*x^)EP;3uzSr%V)9|k zf>R|wTRSuRe|`pZ4sNl;OpWXPAN0 z&_OTvyGtt?YwUPPzhIqUnnMITsVx+)9<=?A{DVXD_5VuH`enY65CsoVKt=pk(heqO zcgPVlqRp=_YhLy(G*S8zWlh;~xIoi)((w+;*FYH*7|*T(^nCqLkHU7v4yEl#s33r} zqo@n+Vn{@UX@h`7I3_}F5Y>C-W*qSwfI34^d{~(T8BLfBwMsD;MkJTQ2biGFm65z{KN!pbJ&Ojv`aHTkx*t`peFlE(VfZISfe~{A_Il&nY z_v;VyZc@I~4r4pT67t0x0}YVf-$8azB)vmmAV!&XrhxfG1r_0%^nhjt1Et7Rb$2%d zv;H_m63Oic|2dpzrd19lIoDQ~J*ULfEOSw|wo!-p1}6}iL@Jw6C)qG#ZVz zYD3j|z+zz<%_>@9+Bb|i-)Rf&*}nXt#zKFl8876bLP;%>sXS8{dRW9s65y~#6FZ5% zlyq|&ZnB4it29Erf6+Kg^htnCh99d|*}eQ?p?zDDbGV+uv_f1@UgZo}-1f$Y7hf8( z0ic4C6^bPha*Vl3h$`rT``+^NBNf;`4epWT225{v9uw)*ihN5*X$?@8G)<7^TWk*+ zaLqppYLjt{EA#KV1fCzFq6DDa(e0NiG;NP-cc&{~5y}>Q5%C|12-73j)*GjsKMU2R zwUdwcLMe!=8Qv#%5K0v3iVadWB)FwYp)-f(SXO4VFd0Bt>EB+pi-HNi=k#-B(WY-W z8)V0*(s`ETv!yakCRQb+LOeT=`xV6Ujt40)e>;BWnc;M1(8pW9E&8}2)IR4Cg*p$5 zDK;9iAMiWe;wyGZpzzck_J6MYjvGQ}iM$h0`#Of)6k}^WV7m8YG`3J1|DLuu#Z+qM z?bD%BdjO3Xcs`LfCkMr8*Yrgy9Isp#xjRSGs$AIveOs6WLla_c4D-AkGqVCzxj`J^ zQ*d?CT<{HaF+z8`&4WyhHFLL+nwX5N5~GAg`#_%2u1t&43EzUy?zIt%lzG;b=W#J} z0K!_jm7%URyV|?Og#6q$(;;W*rzgksMrH?+jj=fkwROFUh{Jd#k9{f14rQBMM8hwW zl&pz1RI2S>13nAL?L-JUw0I{lcWa53=RmGO9caqUZSven9D*A+k?tZ1<#a3H9nI5H zPRofk3bSiYHTg){<;L=*i+G7$J@_*nxd&#Pw{D&9Cf##dKKwl6W$4Z3k8T8A_8NKI z>(xxK*8#9FIFJ!6Umr^erp2UFA&o=kmf)8nfG7riHWa^W8Ap+-JoWI#ZPV%T{*WWB zTma_fQAlL6W~HO}*ZZ%Hvf<|`-9J=^x9&8ZPglf!Dt)ZKeShJ6rhep8`HSIgtBdp5 zw(#dPBOP-#EDtP9j0jnx0(TsLMH!Jo2&*&ncOQ`J5l@uY);`J;p%V02z}=Z+!m2@xu?aaJ((K!iT2W z-bc?`A_C_$PUPouD%{&6TD)lLUotbtKkpRJAa%7sHRl=;1gQY{1I!VtxUCTtC{f_hR(o+Xeoljk0w=!tipL z<eo&>8b<=_6@_^G(-g5~Gvy-$M9r=eza+b0pZ0CjyyAd9S}O+hAtT z1j3UKNHltGc-MG@6|KvGa7btp1l z4~kP1m?4;2#)H-(_>WI8V;`e{s(pzR*HkC5j5!B$eXs=8OVHwb<@GgME>tK?xtO@NX8VXFiqDtvC~V+OOH zQ*4zCH`dlF+NY;ZjOmw;e0_pfB@sn~sP6F5D*ORO zlc_{cuJAlRqM!4Iwa7e3g~V`a!%WeZV3j*#i~`7KL_+)Ck441TZ3V#BTY(L0JDHoQ zh?WZsyT%PEIpT0;w?qp~mM2ZRr|uAD#TC=VJUMoBP}4I7+(HrY&_0)XDD}D^(XIN# zQ~*PDDHD;JYi{}Jo*of6BOWh5@!RYk%-obNPGLG4lyx`YQ*;XiJhp;qoKtd_u)8+} z!tNWRG+~ZmcU1WjgT+LoL}1qY0c@h2(@rp@_Gd`UA;-2v#SH!$x>X5Gi5nfJ)fXE1s zLWkNF+0-gNPM|~S3T%1f(S@2+s>S1AqHvB4cml*}_1U9VKLrp7BL{X8&U-WcP+lo#aX=+GXs~Sj>m)HL^V z41R$g+7OlVW|0l#XxTp(On%EznWGMMO{}04Y|Jg4Avwa4RukuyIT9avWZr4avz*Vf zI?uCV&bO1xcetPL=%4?5$xQ8$5s?ZB+9rh|n0y$OFLLPZ$7^cUMl@-WXOoprBAIgV zJDZ{kQEm2F#ZM7UU)P2 zwe?bgFbVY=8}fBT5rGIS8cR_YMKy<`h#MduL`C4n3Q@KtX~k3~ZGiOD7h8AymFUt; zY>REDsoscCjMY&&h~NY?X$jo&{BA50}N9^aYKaUTn;t|D*{AQ)bMlSU_~Qi074a#V&_ijX=>T)nNEmJ3Otj90BAj?P3c@ zs1@9Y_21^r?|WzX1+oqxkHS`i4~pl{3FC|?YTHSw{q63Kkq?Wc;^itns#hw-L$`U=Q(I|>41Z)5=0G`8^XjG98wGj@U zH_R;*rtXtZr#EZ^%qAbS4!msm8pZZKz9`nHE_)D`>*~L994d34Hm{2F+d-p_BrIO! z(cOyz5Jxh!68tBMVi#2bKUt zs7i%F3SMQB6+q<#wBt^61Fr&b?q&0@bl7he4Oc)Zkjj6J-z;bKbIvk9q2(tZAeC7> z`9~2F+!&eqaz`bEg3}H5-Ub5RCWB|U@4SQ=C{!B9K)4kscrFR8-UDxxc6VNM>01 z_08su@Q^b*ATsnI>1^-4PyuJw#D?w+S{1!3I8B-%rEmq1tk@K8@l&95VRKM&cY*t7 z%)S^b1xlgcQK8mBw;@E}^B_N|BBK7sSTRj0MLCXTym%NGOup?wL^KXr6B!{gwv%V6 zIq^Yi)MbV@H>PCsnH++&HHoE&)Jt>});$gJD(P@vc`#eVDRTweBCp99L{Ok2Evsq? zP(k>hI$@sTiATz8MKM+MaF$y?kOLgQkFVI-AHP9b0l#`1VKlyBQrMPF>DPzW^WybC6jQiD$2ivUIty%9YA)njDh0o~ojA!LKi!!0b z0ZR-ohi7Gem9!VXeoe*nMrBU^Stm`yTug>|T;N1J+ZSo^FT18+k{^nv?A}cODwa|9 z<@(fGoc<;B*1Ryyd_n9y_p{L-oM~ay;&=MzYaTAt1}@ZREHt((G=E)ay;^8vTkN>C zNH6YUT?FwjY$6dTWHgRYGNU#dgNWn(81nZi35}>mMRLHR(4PHVblTL>I7y!}K=9ct z$TRH?t<=2TVr>4R%g`ddXp`nK)u}OG_KmN(sZ?dyuM8aE(J@l6DDl<4_Wd@>Kl`~v z2PD1mq|VM?=Z3!n$4L%wq$O(=^ZrE=mQ>06Aa1G;l;bMKz2@Qi#d~Pig*5G(7f84f^Jy9SM69 zOuUsUKZ;7uirQ#zMOhJsAH|XO+5n8&$Ub|lLK9YX6=C{#+$}sI3QNhiSNvlinlSd! zQ@5XH{0CyO;VUW4BVnpU8_IecF*o=5{ty;|U6#1%8c8XZPm|w%-DU}O{@BG4VLH)E zY0jJMRi6Iq?NgcUcrQblT)KXJn0zfui5hofW;uy5xNnSd1i!PvXX@2PIkHE3>l?^( zf9I+Vr&GQ=GJ+dzBOR+C{jg8zfy3#G>`oix0QYvatiD!zkX_*@8`{*Z5kJ*3N%xjO zU+_DZPr&;)Qqjy>>z6V@K)}AL}_1I776Lk>$6XH60B-- zzri#f!c^7<*~V8q-zVP$5dSG2f_^S0`_L{$T6fpIf^4Uv+5^(s)CS@JPwrFl7>VU5 zifla;>@Xl^v{3=u@u~e_FF~sp{kkEs_f0+KlR)mL!MMefT*Q4&ZnTjKgEvc5{HMS( z^UGl8Z32je3S7rfg$8xR+rJJIhCSP-1Xd8*pvYQ1?6Suw8~1}v_E*4Ed(*dwn_*B2 z#gpf+PW*1d!ZVZAf=J~nzOR;@-oSw-@!HQ`k;=iq!)@Q+*ihCP!7%#=n2OD}?w`Kk z$R48DS47s70R3^UuoT7SjP#;#1hJ?HI0`Xp7gJYeG!i98GFri&-bOxzC8Q{!aE~Y7 z+K1l4y8`dVpO?HID*yQdY+U;|z1Gl@Vgv_B#gQ|8+km)>x|NHNnQlcP%shfriww-4 zWOGIW3dG}@<{(t1Rlj+u?~j@PIlyd}<2e9JxZXBhH20;i+2h_eZIm||nvrU``qd3i z6O~%h<)T+aK`G}ibeEO+`u#No`=bXcUx8GwSWw0*%5m^l=;e31*?h0+iI%5i3s%~A z#`x`4B`cSeb}ho8gQ9!R+fJKc$i;at;;{n8TnhX`(Y+Q%QYtUTnPjpLGw zXHl%--T>=p^>S!_5Iz{-kfusibIhS;KH-x6aIqvOQ)-ArlcV znq5oVG&?$&y?n?@%bOG@IYim~c0?umIg3g4yr(7(C2nsWILkVNIE?k7bfjgHB>pWf2DQDw%dC6w^9 z?0jxeqGr+z4Jn*RgK;qiU8WX^zA^HzFmdoQo-VcI0P?9(J_j0iv1?1@N6c{A!)`t3QFmBbfJNzYI{6nse5=d<1BsV=M|}yy>Qp8-rmDb86a4bDNVq z;FomA@-eGWPF^y5rPRj{d(pm8?YR(*-_)a+mM|kO2Bzj3gm$DlFyWh#To0c0`>iMV zuod1Ve;zfKpUr=cW`C$|z;jP$PbERm1DJ9gQO1R-mX?dCCy<5xFh3NnswF_Kx~} zN)CHQuFV=ds8xAQ8DEMbX0#Q35J9FskP23>sElh-25Ig%bvonum+IIZmRW}S4CY&J zQ0c^T(VL`nNsm$8sCx)v*;Q7dr!c1Qc|sx)DQg4NvLo?S&Jl;{sLNrExyv;A1x-?V zl=N^i@Z+%vC5$T5j7S%UzXnvD+YWgDM2+XLLI#cEM06-;YAK6SytSa@g&RXu$wDY@ zE>h5TJ7w@kc|ObP@1U1H0Z%uVdq*MAh;IOrg(Biu7Z8Yr&s0+t!X4vSV?c*!*N{E8 zK}d+dbGavjagXMrWTe&ZXX{N-DPYLU=yZ4FN z>f76WRnXMTW)mE@H_B-Y_)=OgZ|E>{P%xcoLn9%*aRJ^pBlYYg^wEzMe#jd7<4a6` zcRf~I$_?|mTmikZ)#UtrPbLVs@_|spNtLc~jQ!i2EXpt^`EA>=2g}H8KzfJ7sWB?r zXDWvs?Ci?QY?3(gjJ8)27C(=Z&VB$5RDLU>Bn> zIkRl~=|U|H7n8S*X8BUnMTTK6W_fbv#doHQ?{>S~`P67$?lWCt4R*EYmb+7xJYD*D z_Z{1+CuL67blKCeZL=IB5ezH_Ly99;mF2)wTE`>GUxD3hqPIXdkyz4~%>6VSSaq~i zw?;^qo5KwOQ$CS4;s}<0+A(ZMPpwnc2k;XorKo$6dP`)$vI1@ef%`MHGoOyQh19b} zh={n&KD`cn;{Lp8Av9s8+|@{V;HV+Y8L;Bttl{qUR-j;x`bPy($-2beIOU@7Y~yVA zo=ciMEJT#&!-IIuvnqM3!{irDN(hT5#QqD1wX-cVU-xKQE7C{qoV1*DKXnMjsN;af z7*}mV!eor$@!D(~v8KlocO~To&s;lY_`Zjn3Z*sXW(Px$NBBc~V*d*BPIe+sjD~_8 z^}D$)UQN#^^JY8xy1DL~;hr&%6zuWLvvWPtJ)UvTo9(kq>{y{41##G{G{<=iOHVV) z>$gKw4V+S(`hZU6Dfual3j2uJGtESME)q|`y{CIJd&<a0>`CjP#U(1E`3~uw#ifvXzKl6pJ4ZPQJR#%5wF&!Y*a6~f zOjU{|>;w?F5uBA)@fr_;zLM!_-7kGiTKDyLM|&ylkVkq0u{kuM-bBvEQ!0}7jh4)P zb=fHnav8?M!>6Q9Ts*!b-V~-AO|`QVCEhofM4yEue)}{ZK$h?DC;!N>mQc)XoX|9) zW3n(X@ci+!`aQ3@1+~~NIhc@C*Gc9C7gZL~SC#!f?sP#YE-}~x?d2ObB9r)WlZC~L z?78SWe0t14pQ6~LyzH4`c44Tr+UM6YL=Q|pQ|!ccez}QH6w5LJjrEazPHC&$P?#KR zl29XcIt~Bo+FXr@=n7@2E`0aVGu_V<+acc?EYm!B+B`0mCuv%t2^Pt4MfO(dm$i=DIOj~C8%0;k%&rL4hH}9B0@0@SUMjH?Omm^QId6V zhJ`V6Y%kCo^mVjzMK&wv%NDg6YZF<+f+tWw8H@0PP)yBagneX(W>VQ7PR^fRpYN{} zi6v0oSo+K!kAAdFdAm^B`;=anHqo1gSEEUZO{X!q4QOjeEC+~?mLUCJq8Kex@0LdG zpAOXnb?|m3sp$3wr2#Ka`ecWM%(6APjM*~IuTPo>vGQJZ#igR4FNgl z_B;vTC!VNUJO}yCA>Fxqy;f;9sl$`RdY?P=`gr%gK==A4_4?)Z`q%Wn?CK4e>J41& z4La(5g$MPaX#0Y>`$A$Wc*Ik_GiOhV^@YC|Reo9C=&ux!tIgypIh5TIHPy$^*%!Ik z_u8Q{j+Il=04Ay!NhS#8NAykpRKz+vWCbDo|w3Ks&l>Y?Kv5$GQLZQ$AEnav#bcz6mA;fIQUDWC#4$2i zugx1O_TIa~$!27_FT{5rtSHa8=|*mdB|ce|bgDpmXjkoe$L>jpMMq^lU43_$6gwH9 zN-|9`5nwpoR*Foyb(xKBVhtZcLoTLtCo2d8>kYnG=#4E9KbQ;VcOtgIXj(zgFGY|H zzr&OYBVOVn2v37KxI5_kdxjQEEU3^yZQNDJU>%EyX9?Y2P;%acgd3Ike8VZI@loAJ zSrs6OOfjOqF(vHrvdQ!GD0?yPRsr-gn{XW%v5(PBWCj(F3aR)(v^lHkT@l6B5)Hd& zyhn?Z(2NmMA@U)yG7{{NgcGWe`=bCRd}s(n`Fx7z_YiBN*q6Rh-9(^reS~9lk7Wu` z03ck>;`-XW9tc<9a9}vl_hAJHBI-=_*#}2Dp`W3`sma7iZdVZqDk|(W3GB-;SAAAL zPZLd#)^U;>2yKigjP~*%BJKk{tx!;l4`oaVDS1Y!ZbYq(;Lrs`eC;%6ADdp`9~I~$ zdb|OYVvPVWM|&e9?!*J%M?)_C%p*~EmB40GZGyrUp-QZ;5QwniByA~FnoBTi zw44t{g_u8V@^85t&I-9{Q+f!TOJKB^wi#^=Am8LvWqv+BZcjysvWN^ccJ(h{ub(@M zCypIM`$L9n!!Sa~{NRiBhe?w?V^q&EL?UkYY;K7$-HW&*(z@A69Su>Hew*-~Jf?Mw zTHr0!XQZL0X+gvWcA}4?WzjOfJ|ZBc*_bL&=n5^GmdW-A;#t0;4Zv~lFNqZ>w;HyTj z{zT0fb({RaVGT=l-(rFc?nPLH?=U0jE$iyG(zDfAF6OS|sPC{A!)fIBSWGO*8}_SR zV|=Uy133|uZ-^Y0_?v@aZ*GWB8*#7TLhC&|p*AD&w4hphbQY5sdCG(KW<>YeZv>2* zgW6<~bVi-D13^B4+ir>GKc0TDEKRsY1Y(Y)axeHKNiA1PMQ61XY%##ejN!#pNy>!I zG?hDfOxeQ13sS773eE7|vEyVd?8y(=H5bMvBkpPE8e0gk1wz-!=GXya7EGjYzQw|% z5Kpg(o;eerXwzWRtY8e(x{(y}=z*IT(G%!O?}dg_V!rqq&d0WOP`~T>ujr>038>a5gstBO@X?zI zTmjo~!U4KiXHH)+bp5;DS`hPy2Y#fIFFyQxp7I~_*wIO1ddb+&>W0OMA#X3snH}z&C}yz1oRZxv^&U^2 z5eE@LxZtoZxog7rj6US{6rDP3sn~xvcZIqqlk;hB7L{*(uXmY65n|U|GUm&9kOM2h zmAu$cilzwWZy`>7<8+UpL6|V#Q%fsYf}zdO=rnlhR_rIuNb7pA^+p&F1Ger9V@m;M zmxozncV2}MemHi<;zRJ(5dCT%R|99_UxBV~=Y<)QE6v$ZY!Iz)57%e$pta_-fO;^p z&*H%r+P6>Fo9M@k%?|hsoE;Zt)fXo1;hdE3Mz;}SEt9+y{^?#Hc;q}-#A9$1f#&*`><>Td^^dk5@JAr^>3_Whsij4p&JV49nU zdqI*nkzX5=!vi+Jq}#h}#*cuzMckGX%UIgjBPeIzP(q zVRx``UvDY>ikn}-AAaP1T*(IjHRl^+MBL~yn&qCpF@o*9v@oW(b{jR=r0zQq)Y{BE z4twZZDz+c?LGhi)oY!<;q28+_%ev(IkI6*=hcl?hTvNYo8JMi@l8f(yS?RZ&5s&S7 zeJCHtN+b6O5x;pp6Wb%7Jf1y%vVQD-a{QF|#Dn3)llR0+`sA6`i8tkmWIwTWpX2jC zTB&<0N_eTMlOILAWBPByPhMKOM8-7rAVWcv#fx;uf#0=0CYOFN^u2hQ`D& zOBOla8D6ImCt4GSKuH?_8G!KI#rLZHG$^J_*h2daSur);M#5|_edb5{loIh%mk)`+ z)_lk0kG*rIq1~IpFV~CA&3)dF`tdGEu{c?$6BS1@_y@$?lCl6CuFOV_^5t2d`^9+o zq?Cmzm}O_57gL^;pPak*zVL_aReV2|?040azWDU`qV~l_UBpHGyNiZ`i^jT(rrwKm zyBF6J8={g{-wX_iX_15lb2|ZrZ3xE;c;9|Jp82dbkr<-- z1RzpzU+?#Iz4sSq%Frk;RvJH2r^hEln4?uV{1hMPR?fQt6vr%W@?LF?_Le-o z4X_6!&0g-E;N$Y~zj`++Z@&E9>s0g6_T=54vw}b8b$>2;|6I=gxmy2&Kl$^A7{d-E zrWSPCM|7Z}3}S8@`y5>uYF?94rvuKO7-ng&os9#IJpZ$Rj8msW?tvu!yH#$Rhc|{& z#2!zSIzP*&6?2^toD;2xWRP+lsnI@jFl(`tekb;1>xgfv(4@em%w_sflBjTyIu$+# z(f^vqns&-*0z3@OnM6H&vVAPHSnstyQRaHW`A%FaNL#d175hfS+O)jI^;BfJD~dtf zo%J2*Ji8_JJ`9;R#KeaFBDc45CcZJ2t;LQ{JnOXxHXZSnHMOrcoTz#{S^nfgY7cEP zCjx_L2H&eP;moPY8NIW!IPgyV>E5N^F!B+tnP!=d8)d7%>FvlnuRjoMEcd>-`Ls3Q z&MaX=sVz>;#%dTzu*>CvSHxeQpRnu|2VrSlF6okO`00AsLrD1R5upNLA0l!vSTp$^ zjJfY#D3eP)M+93{9SsHwIRY`t^TC$F*ox~pqlFq%#)EGT$Z*9*=0y6D*t-z@(c{85R`|)P_Z8h+I2s}UI#Rfx}ogRiV`y;a>`5Tcrv2y3g z+=Sb5e0fgv11j%QT^cvTt-a3q3bWGIiwg77{P_*!4w4orxyyrrw{x@Q1j;_SCr#gZ zNPfqb;bTu!QCT}w-c8+amrjFFUVCX{F1M+&scti;OWWmXe(6=i?CeGnwOmg?FnyPok9FT0TytthvdS4Ru`XVNwS+x2{H zL4JZ*01Uteh!7hVyI#I#0REZA{qOj92L2Dr0OUS^5CTGo08rP^0$if7f#9}*H;LJ*9R>6*fUA03?#5Ks^i zQ9+?}OG_*C^ekW^>Y16j)3b~H{a+g!Th%qq9Uc9|#563dY-D8gj7+TmhkZr=KHUF( zG60DNkY1Nt#Xsdn#jMx2kLw60VORB*5w+6@V<3jb&v~E#KqkU~QW*qFJ07N+#`z}8d80$5(VAWg*e%Ul*|eaaSTHaREn-rr0|o;6Bq~f$lezrltQdrs$`HVW1OdSU zREA0c)Y_2vwk;I`Vk2rB7&dq|l7tzQVw3<3h0;?Hf&SJfmkL16Z z0f;pKd|jVL|8;#5g5u|lxU|CIgl1%X)SUf^2x*t8IpZIOXyTY?SG zcu|>huYeh{61K+Zn_lWE=nA&~!7b6S8%zzXmV!9RaNuWRm_hAsbC_TSvy ze-Hn62L6AafopD-m<*=;|CXEO250}@xLILZ2=@Pho4uWJ`&03BiAA-`*MYy>Y||l` zZf)t8YNyHKhl90c3w0hV18j0W|8TRvw!RM5ef<6zl{G`IS6{L8FK(7`8`l%hrJJeO zQ1xR_o8A7~P{Utt)^J*|v1W6!(6nSm*Zu?Qa=#Qa``9g_5%G zO-mpA!_6AH2Q>UzAItgRF5J@idwY)3babSp>Ex$#9X^!Zu(kOte|faTdNe5D{P>!i zy^9ZOy}CGkJUcr2`SYt${!MNi3PkUO3x@C%;zG#mXl+7an*VUKcbt~O7(&ZWg%`BQfK&5-YP-xDuyuys`3HnTT&SUY*`~HQ_He zt0%R&`o>6;??;l^9p@i!EgXw}BwPAy{&;8emzzy?UQ2b#FIr1;tKD2n_vqnUfA2lx zyq@8^Rq~ z%uW8w%@%JJm)36mJpwh47Z{caTY6bF(ll!GkW^Yi_oi@p0)v59^DagI+|0 z;9(#4JGaArWI^fSfMDIu;h;#b;IEUNJ8 zJbHyr7QJVmoP70NFFV113pm+5`HmtMKEx_Napb@%Dj@43exuZ|aA{Jc8BMcl-nuD*MUKieqy zh(F({`-#8U@4flw@@V$ypR3dLkALu&CqMuE0gzz<#M*5@=6w`I4+~`IYx@U7g~eb& zyxQ&1`}-k`Ls%l|zIF=#{ZIs1)^DJ42X)#$8mX5Bz1!D8-?$$p5|c&tSi6&Xem`7h zD2w7nUnl&0KO#YyEE^V~-Nng#fYH{=rheDg#UpnRX%v%9TcF*|fBzuLl$OIvyRf^- zqcZy087JckXOC!WUW}s~Df47~vSgL0kbA(&y=7!?)mUYmZ37qlb9V17pS;&Wi%RT1+L;i3D2XpG8z9NyT1mpH>ZeE@K+xHI>Q-*SRr2G2~{14w?$?}j|Is+zY6Nv-_ ze;ryFFa!|fC5QnC6BGg>hOh9$m-x{u{P-1q5|5w8&uJEf@_>C+4_7#2) zk3YcUf8p`xc>E>)KgZR7Uh&^rzTWy;*YA3P$DiTxr+;;V#~)wo>gxB^)zQ_}^(P%7|Oy4dMG-5EaGn*6!>b$fGdYx7^RvAKDze{LI_TWcFztLxj}*7xSt ze@(9c9$7o-Upwnvzv$Yy?A*9&U%&eNSFNj;O-mQ`U(Y|yUsTLqmdsq{&s<`sFVd$j zlE%(s2hSq<&QV>b-Yv(@bw{S9`--XSVo?j6{==-^eH?y6HzQ``6Tj(Yu38nWxfHMX zmw(4pE~ZpZXV;A9eHtyQA1$pPsBY-0ZD_4;XliI^{0}w!E9x5>YU}DhR@M|2f6U1% z&dJHk%gHOs&8^7GtjkYpE=ubtOYf`9m}~y9(wVi{m;G}%_t#kN*<}9dWcKM~*4aen z`FPsdX!6-`;@QyaO)pDC}`G#}Z>f>0&zAU-V*|%!)a*Y zdt?>+rNoBB{#%l57SqQHSjO;p`XGWIv4op3M%|{3P^Jl$ph1by_}`>;667-GN67K8 z^Kx-^;OGQgd^Doi- z4eGx^bsaF+{}Kfy1r!Pe5s_R^F(80|2`CAHECiq%1VB*&(Cur7P9^!LvqfDfq=oLk z>TEG?SND2I(TBx1KArs*^2$SguJS)RTfAA*4*6DprP5FHBit>T{HMjq9XyZ5jE7g*QAJE^<_t8il5c&%9bc zVe{n&o7AJq@6P$HhUzOikg?Y|s) zxT)^vcPxh=1)F=qhTxO+F=hQ6a#!3~j_wD8e;m3YjOLO|&QfXxtAl?k8GHZtvz?8} zYlm*V<-SYUpbOz$dU;yw->3k&!jg_Elr`}Fb!fzseG+c=z~4+-oi50w@i00Gi*Ybq zp}TT1_^(4xs)WRcbcH!u)9Ve76QLCz5&YBH0?FkPA1}Tfk{x&gWYHJx?jaTb$D#LB zI!0s!mr_J{{58bRB zOw*BbtDU1#p4uY1>`=y2btylLR54IlkEYKRuZhc!y}q(ytDL*@Ng2wUdNk1EmT#j9 zaJ%I@;rcOB8=%44Cye--yJ$9_M~Ho7U-qfG+eVk&RU1ag7mm4I99*L?*-MaBHosL; zs1iKPo=4$HDno!v`$mqncUGb$pQC16R;*tn z>a!;p3acz!H4C*bhv5<_WlTbeG$hWBxG*5uzU<*?2UPL^4i~>@MU{bwN#pB<3Oqxm zajSYb<+D=Hk5+(hNqA`7w5aqve)Oi&E&r0RZ7N?Jr!Kh@)bWD<3wjHx6oeE(O9=Ol z9hOzD{&E=z7K!f^5^lOPSW@PuR$E`1Dab6i5+wzaj7_bdaa>CGnZlx??06Uba^G$`o0xBQqi;9qLMVcv>!aq z(VXf6M!Dtmux?4~k{Fpv_&6ZoY{*+5pPK?Z1@je;QuM*u#u8`^8vxatqInx6mgbs9L@qiO9%ou|qX-)xtAYxK zF6`t<6T}aVflMv$1jKU8Hb`GC=}Q!i^>C@Y>5B{R$;0;Z(0?v6j?d12AM@Zn>*qVe zWAU8KVKhe6GFe0~M~ocR200L6NP^(1VH%9b9gIoQ*f*y^%A2Nd)#)lKuP&dyV|niI zC09ksBmO`V7Rv+IEB-7{JN=;cw3CpUkGV0N$iWA*DcLrlFV=HdD*DLMW;?!0C2B9F z!Z{U%@`bo`?`KL3@gkn*tL4klSxc819A;OeU5?tbL}42B3gJo?WFpVkajrK_=@qA1 zp$p0vk!Dv6p6Lc%n^CN~JD)qcslvvcKII*Ph_h$|EDTLJlW5KV1f+U^=i z{$Zapmzk!vI$5@gC5bu#4OCl$1HP}QOx6dxkur)lZQZMax(_+6$NgpMP!@lm5Ky?AL zkaX?}5dvx4_=#jTEZa17$o&y-(&yHMIz#!l4G+q;>HS&F1#8+RY1czpZoQe^!Z)V7 z*p)L&tOthYqA=zBmRKMtCFw{U$70K8fxq+ zi+U?)MdL@)%Xwogtkol1h((6FZtXb}Cb~)Ap2g~Y*d|XQxlGFcpzbZB+G@A8;e;T; z1En}2cyMSc^Nf3GM`Ur?^|Owzw6y0yUr%t>6xQ>9e16_I}^9&v(Z6 z=aV(Av2w3_t&uhI=gvLnyykUjy&4tj$N8G>veHhSYvQxnX4>EdH?=lp)kEPoqek8o|oJs{Ec_AHFm>Bsz!o34kR z$}eo2Zelfj@4d<*2L_W$*d*|36n;feWUV-*C<{!cyUKlicJ(Akf73O4$l0mn;j{T5 z?!%xDtj_@grq8+oM;rbP9I9wG=>!nkOp!}w-cA(TR+%j@bW=DSkM1SHRZKZh19Mhu^}P7x~S<0+SwsRVjs3gr*uZDK80)d-El%G+7Q;*LUmX;a{_z~ zDdvmoMg?rfBosbvut(@7g>n?}O-bY9Psfj^a5;N=BgprHabbabw&7;>1W~9L2#691 zEF2pV-g|-{W#Kds5iYriGJK^{(BR&DV7)AY{yKx8gagYhM1ueXUzz#OyYZeKncEq7 zRBu@uP=G2uL<=!7uUtu_PHYRO3~l@IpTYu-LTvDG=ybbz#)XV_ ze4;D$95>)Bd*1Oo8PM&v_@8R=`p5AHaM%xtgcG$VPQD2jkg$`s_1gOX87rgw^( zk%LBiO5#Gw{Uv*Cn$#$ir!xq=ax!I=M-J~6j%6PYwGvT1!MPqa*%F~8xdn8JbhaH&pB&Q?MpnN~K3O8>p zlTMK>BaYKNfhMqrhE{PNmmeJE*p_QxOK3PR$nhlm#4rCnEd zpLGduWs6p#tS&4J;l^~x1@%mA8`U%$3V+vH1?drXU?B?sG(3Ucxa6mOs^PMoamUN> z@M1H9TQ#gwMafbd$CO)f3-!!W$I%q$<{+XK5^`t`#?>r4;CN#6ceP9+{~&8^htE8@r2*fSPpwXl0_`qj!E*-gvY znK(5?Kmwi0+#3I!g>b2Q4*yw1?VRWR-*=>xFLEx`70dLhUY+Cc1kSzUa=vxr3F!M6&VmaM!YR-QMTd0EFqAExkpF!%(WbO$uF%)>#D4_fXX zH-N*hFVFoNw4~DJIcdks3n9rQE=hPX+KnBBq43$zLWR2WC!y`sLJRe}aDu|kiz2{o zzV$Y;IT|zHK?l8vSX~P@rHYt^NRPS`uX1oLqrA;3i+1PD-mVomOn6d>Eq7n4YS^fV#cBHIwUFT~X5h!5nmSj@5Eu z?Ji!AzG$?kW>;N%wUb*A_ExqR4Yn6uzRWc42=p#5r?V^R>@X_rsEvuQq3b-s>TK?8 zFAL~&v+L~GW$OCSNq^AU?F8$U?!wmX8oZa#l-(8H(KTYE`4NHFaanGOFPnU`W0M8* z?0jF_)sr-?La-$8qIuuho1oHHvT_YhyBSW4p?djTvSn$tE5B2yUH@tN&dZpkW|b$o z<>-bf@tRNEZx33zN)b}m5U^Ql>`EUX_q~enkd#Sf^-i|*xMS8NL5@x*zOWt-)TH27 z2i*aCLy?8V13)V|0!akGj@9NmPxA?Q^m5Ii_S#r+JGuwN7L79U(A#b5*w+!Pchk7k zX%$H47v+03q-Ho;SH$ExgI_GlOw z7LZT*q1mZQnD$-|kC*TxwHlcmI-_*#`8_`H`{{wraSdi(W>6*=Vz{%rMX?S0}K^H7AIglB>a$1P#E>MToTw ziwB%3$AHaD99wy^T5}PW4w_A2JV3>}wm(OQ-K(SfBgN2r78@*9W+gFQ8P*>P>I|jY z)Q#JU5pgZ1bcmGP{IRZJ_O}bM?8DjPI}PI>#}XE=#!?VipSHGLDST^)3mRr=|4?FbJoI(&Q57W$N1j*XeoM0}Y7Q_6l8ek@d zScjKn#T7s2R^jT?yxH}<1;c`s?1IhX1-qbye=1wlEQlq|X#_z8$ML68kf3_?91P(y zfa>}6dlbWBuR|i5D**_{O|BT zX0L>YWn{7%6@vE_(1(BFNCEvf6wyb@e?bL(^bh>GL>(!jjuieM1QK=hf8bxo*QIjTrOIDW?YdO!y3}}mdGF2T z{q9S>-b|=+FBd4^(r>O_eQup0betD*T_etOO&;0g0Yb_-EQ$+TM=}Q1?WQHDQ-6HHTJM1tg;xISzFfZmX zKmM>F>8LQ}s3`5IDE+87^XO&vQCZ$mWx-K((NW#Yqxy=YhU(+Sy5pwCob@lpn&H8oSx9f&)*UcN(ufAP( ze7kDATQ)z$gc<;B(I<@M#&AKYdwh5zXBTA)L~gcScSYw2d) zn}8?%Pd#4j5#7JDmQhJBwX*)w<4rCZPm}$VwM^NV%vS!R$Lnd;e5=Px981traa&i6 zy7@5L0()O;bgRdE&z-xc&Tg`Z?N41X{K?wr-+H|NSyu!RLjPG;{5M(4zv_xHP_4h~ ziW@|wElvVew{^wb_G7T5xBEMq>8#e2(pHi-=@jxCp*kTs&Yu-y;{t)JY%dvjcG!~Q^ zmHa@Yco?nq;-`Z3`xgw$UiYOvb3w&{`x$9kG9=_PA0jMbIaK{pa+@aPr-zRV1NO$f z?#u9w&1=b))x($k2^NkM;=ESjR7^6XEi7j7^{QF)+EN*+e!E6rCqxF+eG8AhJJQl7E=#-;V#QjdSTwY>8W@3pow4IQ(#t?+7D z`uqeK2}y_jo?2T;f)=G~c($W~BqF?;SUWF{DLjTGP4iZn%N7f$-b)^o-@D)G8Fpv= zMX#@+L+a~}C6WK-t*mQ~HV9IXb0(sH{Zjzr0oASLf z>6=YbzrSjl##cLgm~ND)f+OBzsZr(V5LJ-KZ{3>(PB z0{c(;lKULjeE7ll{dCVPz0iJy;%DTyhV~o|8k1rpEGIFUmAjLQ*LNOPq;0l-8L=fl zZOgKqqipRzcQTfCqMX|(!t)Lr2?R+3c-lmu(sMwWHO~7`<3!@XPYGUt-i5i1m!gB8 z!a{Bgf+%R;W7i0p0fOkorWJ6uAwrB28`#jTD%ByJE2U=z?ZAXoeRk`^ zOLOJ^HEe9U-jttF!KpT$Y|OIVJ6t zjQv&lb$u~Tu!MH7$l*xM$B;)IA4VI74P_Y=2uxc_!B z>=U6fP9tei?y6|5Yydj|{uiV8_79sY{qUAotf+XOnuaX}eU$R$=J)9p+t}~nk#S_v;qmb? zZ_2@sA3x5{&g27kc6WDWeO66s4rIJO%Xoc~^V`_l+uPjS+}hgeUD&)OBqM@;Ncevd z3ENH^{FOhv`}_2I{{2}B=IHhvZRn%b%irw%H|#yv;z4VOpzpH2UlpGJy12M-Yuyve zIlSdC6B1H4HMQMxnB!-!ew^OS51q8_-n{8LwY}Y2U``_6U43|WeE#b)7;|J)eR=-% z{P*Ey@a?UF%lWP&WZM-Ief#`xpWB&|i}PPsjrAu+x8sb{tjVjF%ZHP1S7qOB|M;=n z5V(WPID4~seRz2FrulelYcr{NTf6GO2mMVfbe%i%kSFzkv+$Td_ZQp94S(`4!IEqiG0hZ53&IVpCtCqv zfgsXiU1V6QBn;9@DKa!V-;mfWx&%rUvl|GgEKU;j6iZoeJULz(n4o)z^bR7)PDuj8 zp&yJ#icRM3xikM#)-Ow`LTD8NP~wRn_it1)3RQ^*AjsW*XoAF;*aZu1@RQl^+blEO z+lvCh$&1u7bp@DHxfJmX+uhTDbmJK|+LLwr^->bhL!Z&}-bF^><~;OxHU$hu%+Q9o zugoa7JmqIVCjVT<^x{*PV~gxzdOm zfoKsG%UHBTG#f}FISdedr$h`0ra^5dXMje4%pek_l`@dy1vVfLG!85(ASRQ7Fhnpr zvMCBcR<@8<&tMQ>hLu!BDqX)e9>9CJo>S3>s2&IfC$FtM0*kC zwNM%m3=4;O7$8iPRHYa?20$?NiP;P@MY1O8ApTl9VE~Ii0b(kE6DR?Q&_^Fx1|KMpaold2wnHK`JhODn>5qI~??P z*x1OJ1xXoXNoej7QJ9dDI+5Y~P~ZhoV!xmPg~4#5*l`nth!bURbzo>SwV{P39Hq_@ z6{v^hvCm$nN0j6ymlR}`6c&^em6jA&l)kJiFRiR7ud1miZ>%b9ug>eMNgA(@T4)Mc zX$#)ziQIpebUc%J{-O9{rSkG??d4|U)ppC(_gB|H+OB_fUhloRKJ2+Z8Mr!`xHwom z-TU-wdwpkpd;QDLkIM&(b4OFNCj)b5eV;FSx2^|%UJoDLjGW$#p5IJd+{|9wEMDAv zyt?^%eY0`(hr)DA82WWfA-cLgxw^i%yuP};zPY&i8@J=Di<|3P-qGKrqra#}x0nBw z==6V!*Ys~6{{3%f|C;>2xb=_w|Jwt%8-oAyt8SGLa+qCqO)6o+`REY~l6y{A1YiSS;?Ac&Z=!u-INx(i?8y_c9mdw4*Jl?{2|4+`Z|z-JEMGx> zA;fC97vi4^{;?{oRx?yb?(?aTx$-lg3eiMqO=daaT?6dZ(U(pFGxYG9ou$`Sl`i+p zBe0`_%c_!$%)?ZeWOAri*Lp$dUkf(nJ*s15ceE6qLin`Iy_H6`Wweh?Ll%v0?sVNz zPWzD>jXfY1$$945k1fa~Uptiudq-0!-_N?auh1M(t%FN3$BR@oBO@vSQ-;#3;c$(t z3Q_`m>NjFSK?h#iEM=d16dfIE?6g^o=M1J;O)xXLC==Ioa7>ZuTKEfh*;WfCCmA>E z_+wKEN{Yad;ckW}&a39Ki4Y^)$88llgA`l~eZy#L*pM!JnCl}9OB_?$XXXGI1iiuu znUXp3WHsWhDz#+r04JDDHyo?plpN0~0{lLbHkVV#!qhvZjOK12TM}-vw*?@Ee?9Yw zcGxk`ZE>q;;P|mZv}U1Qf`?3+8fCAf)k0ZeI5SI}*P`Lzo%*@2991{6>>c*SLc#t9 zlr55txS&QGAvM|cmPSJ3YRz7iCvk@Dx@>5Qp}c_F12O`c<5)@U%n_O*9)-8J#0^_# zvyx%^-&)JL6g63yl~tTvhRO|2;7yv_jAp=(?C(z>k-oU`<1q7iK(ANK_SD{pvto0B(GPowW8KXSPN5F{Nj4m(lV7ZCB%%9ojI(8trUUa_Uf-U6j3H~_o}kgYLY@Uf z#sg%IyXu8xGng{PK(*E@eSI3oOl)m;Cp+ncs3ON!?3(wjdA1l8#P#-Z{-+~Q>@(nSDYvYpu4?PWi;khKu zP51N4gX_DYGYq2*nj;F1K(&{c1h+99(5uL9SXQY{1(<=#?~?C|u9Mg4O!HBa(eCPL zkQu?k_#u|KzY~e2b?gTS#=BXi`sk#e!tlFnRguSPl=Y?c z(S(IPVJe4V#MWOU=_`4?W8g8)*y#RyS3P%QT~R#LqM%G0E7D+X92s9Gt|hg5F7G-` zZiKcP=`H4^~Qq)D)E2_2NQ@A;7UH()*b(avDlNMG&V0fq{^@0&OBQBeb8- zmqrn5uSvDbUO^(4r;p>*HbRJc1oJy)l4aao`8N*|VXcNq7(U2QqgUf6cOF^A?!!}{ zz|%CVETGSS@=`;jRPaM(B%V)jE_Uu_W{}Z_maBDGp2A_4L1L*UwP+fVH7>mS^ z`u&Q?rkpL!GPUN43GWUMrtg7eGMh$`kj2BivzxIpxzmx!=O?pZHML+wq6PK~+M@!n zRymStbShlxs1OoYuEe3C^PF4(Uu_Xc-Hi!hfGOZBm6xljYRn{M9hJbfDv)s)Y=(7< zV)}6f5>HHMaOF{{#CXMhpV8Sc@KNE|jYWi?SR@?Ku^~ zder?=XdX?TH9BAGe_X{biX<*@L^D{VfEm95b?^xmT04$wT*o=chaG`z;MF{&HkN@b z?fW+u$91w=SVm4L?Di8>xjLZQit62BL>d#pSs<65pX*#eBSk~pc(t8)%w!j0Aul+L z%aS<8Xd>&RSv|~{_sR%Y0JGYpMt$Q%>}>I&OukQQiv2vt4|q0I5U9k@y)Ubw_N?A;?3y$@|B0{J}!$GRG3 z9sr5~`oYGwqXq%SFv?q@LbxMYG{AWUIH6VUdT8=7Or?Ob4m{wVO@{?3!D_>m<>Md+ z1milt>cC#u1i2=naFdz4Z^rl{KN)`xEqVp54g3DI$#}ks(yQu}zFtmQhY(vvZxF`} z4wf?|fC#^D9}BCO=LN(PSuf^`w3bDB#YB_6YUy7LLO6Fr2ysNt`a#=Bc&iMk$ls_` z7(Bq^X!$Lx<|J>(tMMl3r3{9vu3f*aK+vu8UX2l4V(4lN#f{uY{Ss?ypJ!8h-o$OQ zGf^}(SECBIQry6G62}&e8Tb*ZY5!eJvR3=e6Py)bG{6Ey(TAhpgohRG?^y@|Vi34* zcplPA0DyFWV?g=tCiPdJ{Oq4cWpC+#_dym|HQ-~oT_K7%5{rsvuu1k(20+w>)hmA^ zT*Up_bTzPd@T6vlha3aco3jEeF}Nf#{IGH4KA&+7TjoG$M;m>7IJpZIjc%gHVU7$2 zO2c+=<^1(%zDnF?D4%tH`4R1v-rI#IhHCoN1$+cEUiyxE(33j00^6MZ=rV`{PceB6 zepE2L+Pc|xlkU8Yj7W!(x;U2p{{EFc#{cun1u>eusdbbU>JF|zFD|kt+Gy@;=kE#c z#^Ru#Uc!rS`iMOh=3+N5Kl}Cd$F&YH1YwZ3MuM}l?nT6q14u0WCk}qFtNP7C>Ydz= z{2ZbRVhXql*k^A{zqr6D1%L&3GT3_yn@h@pC%`+BOQVCESi8XW=q9V>a$Y8UexEhoanHJ}ptY0G(|48{5CPXBLUcX<^Qb&m<0sVEmu%ewf(fU=KC;+BV%<0v zg=gK;i$eu=?almhe$RV9?JtB~WsW?5mR#KVeWT?`otIX$5yaw%c6J*Nd>iBWP ziN{3>uo%PFP+-wHZ*mMFT*SrdEPPtH_n39dBp-uneP!OH;;MpY!!PmTWPpIaKiGuB zv)AVVWQwar_1VC@v7P~LC;-=500%98>&l{6bC)nd8P{S8-$pyk>?}-5AJ-6UWC9_u zsK?O-hdZ`~vnb;x*gnviGR~h0b2g0dR?{)r!>{#<2waRHdWl_ng#BD1GSo0K{Ol3d z-yDBRfaafIDCw5tUj&GNkWrD6Qr-drp`xImrl!6nQ~xoFWMO20GcgD-F^DrmrI?`d zFsM8uRGJaW3B7fSWMICnt^Eate;)ssU}a$7f1DzeHdvnr^v-cw=IQ)V|)f;%BO zeH6IDDyFk3BSe%DsjE8iDhx`j~RX-ms4_{puU;QVZ#*bai4W8L( zK66xj>MG;xA?oDA;~2zd5zJy3$^0;y=|L<^GY)ny0j7}x(@2Ni%VN^ZXHqL?)~jYQ ztYb54;jnJ!a%ksvY~yiy#rv#Tz`0TQX}yF?jhttNlHW`9z{2~%xevoKA4R4aN2i!4 zq*y1WIHx9hW@7wv;)C)Nf(jC!7o`W5<5n6kMM*~TuCduX7%!&Mm?v-y=NwU=O+CZk8iChhp%i#uAaQR zau~aEo4WL$IS-jXi+g{Tw0M@fc$&U+ma%l4xpbJdw4b&3Bj^2A&cfH+`7e3%pYmpx z@~7qs#@^+R4(1Q{<`2EiA9`Id)KxgtQ84(bV4%5hsHJ42_2o!w`Djb^SVP@JUE^eJ z>vUb$Ozq%8_1I$7#8TzVa>d+-^7#*C??03+ekfb|3(FtMmOqp(FPD5+D*UjNx4f9U zIGeOI8NE0bvN-zu{jmT00ngiDi1bgj?BBXq-p#b1i~MIW#M&1}ul2WkC6A5}`XBC< zk4DG-;a=&3^_P1khBM)hdnIR2=KWiafAk%$KV5jt^MAVVA`IR}Gwh1;+;-s=3rY6; z-G%pGt7UQx-v7~scUvucWjCvlCnF;C?OleG@x)lW$H;h|V$2(`|7{l@@x5ZRasPsy zg?g>G@migS-OjIu3kZ;e#L>1l79%9sD_C9Rq$mJ) z?nAYKytF+`42KG+RZJTAD&2sZfJONKhlWO>WDl z5$!zxQ9KKjCXqdBVGT_Oq$p~FY9%NEIhs{Mpp#YZ=UypZTH5TEg(>rgZh!JAS+1}{ z-8^ok&8YwvcIZ?$mZ~S$yxQnkuRRv!tge%NGx{wH2O_;&h1j4$HNNd_ewjSMPFPdb z>Ft1OdT*6|=hY|5<%c+0B(!|a&3X48ckH}*|FaXTf$&%8VJo-GYr(aEpRX?{Km2MD zM$g1k{l1{x>&Dp}b?JR6r1`yJH0p^H;>6*Th_pC^RIN{SGX! zaY7!F5y*CDr}{7qi|?&z)1Y`n&f$pkhmXPukl(RJQ^;L|=13*RT!R^n%Z29Wn)18` zbGn3&j=l8EYxEb4tp~i`>je>eyIae1crR(?#(8@<&3+JbbA0<)@1y%8gVRQz%j{P- zuL5E|h(GiE{rK)Zh7xiOqN;iHB=udlwaFpu`NaDm94|yH+iK!s4^uq7o-bS=rDQ}N zW76MqtLKj-?ZNw4Fd_GnB?V8)f}%v?D}^L90Q&w)QCQ&x><~wRVGHZNDhh0l;T{+H z5-a-bOes>x%H~K1uN+~O{FB`U~RO>6@9jA_hho6nBOtK5{Doc6Ing836A_3uwavBnHKqyOZgpv;%IibZ}UG z;YtV$Gev=~!WD^|lJ@Wl;Dv-0yi`JY)G{GjgI)~}%rv0F3X8-DFq1b$Vn|9tp!S;9 zIHHg!uc>0_?}CrO#qxNOh5JDc&0m3hZTS4_g^W*!@xCr@sYvMF_wtkZGPF}qDqIi( zJ>5gTBf)@N3j`U>&T?^CbyP((Ty1z*BKc}p0fsgpYdk=~VGc*P%vO=#RDCEm_6%AV z4oaJ^8sN%4MKZdhsFn=7ffwC`hNHT$mSGjx-g}}k20*}wJya}@x=U*%fw-;sUjR&eMv7O9O(+TslehnNI|r@Xh936TNA zkP3q&<;+o6A~>uNz@*>8VII*cQ~_F;Ac#QVeS>`gZmq;?(uM}FUaB(OFySW>8~`X< z;3}g2D9Cax^c)7l7Fm5mU&0cft+WVaEld!CcjK&J3K-`SdqGo0nXQXVM3;{wEdv#C zvmb_Ikpc+q>sMhSre;7lUm`8=I%SGIS|PI#*G33Ln|jvGQ=%ZL;L1#bx2P!CT8LUv zFDCA>7-VxUd`lv6w=x1#N&HF*Vc)EP<+p;vIXM#hba;hy2rwl575{;nW)h^p-Uz0| zG?sun2LrKLQ;>0BNUngGv=-hYZml(0FYwCi;D)P@bA>|1FTIbu$qA~d1ZITw->v=lL{L+k z*h8Wg8ecE*I|mX4^=ZSKM$z;2#H+noY`t4zE7gs%4$-@4>+#}Dz)Ld4+cOv@7gh2~ z2#ggeN7DSX6$&!nYFFH{qS;S%0kjy;fdELFm4e2c*P0NLmEQW{W`3b&Kf9ZQWtbe# zk9tz(Om(1tQ>pVh?E3C5Y z9SOB8^{F;R3#5DcX-5ELElBX8Rtzn2Wz>2#j&x!yD<2plQ$NHy_x{Y92XRWgcxUQ- zEl9dL@|<08@7-tHPFxDoyUNWsKj_zFGJ47Mes0S5XsI}#W~Np6VyB*JM^6Mv_Y?YI z%h^alh+UbJh}0s9-$aapoQsN#b42!M_Hpt-*TtKW*72l|JEhA(Wl6rOKO`V?xa$S< zduQvS7t_{Z555vNS`6xuqGBABTws#TgLD*0GN&XDBXWQIM74Ag-E&1i&US_R9V!-a z*Lx_(Az}?7BVXDk8Jb6@lv+q3F?Pi4I zLc2^#aP&j$B0LgHf1Q5IG;z{X1q24)r=TI2t~`|P1wTXysk;MeN3%^YK6}Z*5ZJ?b za|RZQ5aw!pd1&{A9(v;^m-q01{-`^GN@RNZ4#Cn`RVb7wW=RG}?vaXhWhZ>wA{gu} zxzG4cf_Hw2$U(c8I+#itt3h-8khrwK*KW|4|LA$O2!!6%as1AGdKAGl1zNk*4-X>I z$0UCw3~8<>wtdAXDh#%!l5XpMZ0I4J#G)!tgbpsI;a?OGjN}tm3lVMO6psv%jHKmj z3*nj$kvpSNfYT{T&>{>&P0*ovZlM}mWSWbi)cc{@_GG#eVI($TiXmYGg<&dvVM#h+ zW?K*o>TnK)aC^aUy9|hZMmSzWxU(VT*;#lkQ-nELgr|Lkd!$ej=2?X*{)Q{b<{nP6 z9v%uDX%7hw_sn3Rry%(yhW8r+qTK^;z>p{GQ7IXbOH350P56aMGC7e^k~)ldJ-{3| ziUC5hJ1G65JGkI1Bufn<-W`bp#KLI1kf{n)FGlAxMPoMr^IS<7l_F7FIDTa@{5^1? zRdC&1m`8Uc0V;NiI&MZHZq6`n!8dL(BW}4Z?&D(IXNkDK12QlG55PkS5C*7Xy}Xr0 z`Dgd6;J=LjwA2a@i-`1#OEyVM=S#{a$;!maNQa9^2g9XMtkOYjQobDGu5d9&PDuxT zSw~?Fdm$q`0Y`goFBk40ckXa^?le!X5^v55KaNU&_Hr+_;-?%1cI~jX&d3aBWaWXe2{5vXGO|iCGRs1l5m1;ilvNGN zddo1;g|Zt!xr`V&Oc~kD8QClu*=!lvoftXY7}b`p9=#w=gXdg^FL;c@xJ)D2&Enx^sqALCtfmDlCdDi!rL2#uSS_1b ztUFk2->^9Lvbqeic@MHa?_~+;W(^;J#|*)fdbl#%IP;n~3v0Oxt9c45`SMBy(hEhB z^MsOecoH+=@oDU_$?UNS+zC-)na>r9J=H55?^QW!);eo8x@k6hXtsE1z4Fm&_t)xr zuK6ZZqbEkaFHLPAM{TfJW2jtnxJqld<`(ycYt)A86o=~&Lv?b4b+Q9>GJW;3-Ociy z9f&w%RloMzZcDKluduE8lSIzH&;0_ zTRuEfHaK0y6(ye?9-EmQotYk)nRz!eJ25*uJ^T0ZZv!&I=a#mWMFZN>%p^o#xsVD6I!;Qp1&#s-laz=q+satyGlq zSS<)T|Nn#ziwrqoy`;Dj;Ssg>C4fJDx4t<>JokVq%)1VBXNO4hKg zUMcc%{f~|qQGbre)BEZZ!Hs>JaCIT64%r;L$w9EUGcE-pln2<0h&GzxTj?t;8O~8gHK) z``^qq(^5{Tl4Au|oMfefQ{&E$S1aM!5c3nxQteRX z&&zW!lZe^W{d;JozZa2-(2^N(r~}O)_sP-Lr84mS2!ik&3wCU>r zW%kW@GQHbCT}06b>{?b0=huT$#yXt&+FP_YOO5)yG!IjD?0_0BCE*#Ll}xAK$?wqf zY_!a$!EfEYO`iUdMfr*8ko#1aAVsVmbZ0K>k1UFT$HXkvn>j_~R_ZP`*UqghN1 zS$v8u?AR#GF;E5LV^kfXUGk39tvvPZbf`xMR~RT55+ zBx5;egv$nVo^n1TVsR!Brc01iAlF!5MiBUvGY`1Mn#1~e%UuD2b7)S`NHOAO1()w< zA*B!m%xK_%k(pb=qBU9v!fqSfTzl>&O9K4!Bw9rX*6(F#57Xf#6-_q2s_M;E5+||Hc!*L}K4>3{ zTj*q50;`Cuo|OZF*NCgIP*1rf(@kS*S5g;Q?UiuIi3;bF zB-v57*m0IB!6itj%kIdqL~95%70AfgBZ-;siH=#3Q^{^3it2F@_9^06=wNCrBSLvG zo5Xf$M75oM6{wt$AhMekc`?F+E*!^C*9oJ<4-QjL;k_e0z)AE%EhKb!CKpSwkB8Zj z6{*<^do;EE@T(sHO-{kICFP*N?3S--T&?q4ql$vO=RKO|VU28#JfaS$P{N>u}hegSbpwUE4<9 z#Mv=>8WVmoupZE;9kf<%H~AcT@5{yV>6^3hy630wzT8}#z6Frfqwq9Wu;|XZ!T0Nf zsm4}tr6riXBM0RN_g4s>JRzPE7$W2uvwXdUhQhb%BY!c2{YotQ;d%F>9^|Z1`q&Qe z#k(0auMm2VAqFLIN7q)!*61(WIE=T#q)qp}GVfv!s@!+i!c?!b*M*MUk8f1O79^#- zcRos35h6F7c#kK5hA9ps5cXDcLn!-v%>I7UP1gI{rt8l0ao5|X>&-(R7- zhCQZ@Q-o7w7yjWY;G(-V$&!YBv#0e(Kx{In>?15_78G2jK!qcPV3~DaI-zvfKNm;Wxszowqj_T8j|18!9vTJz{213$#(7U zyeI(SVjHzI$&Nooy8`Lf@>E;z2FGxHDI5hE<&+)AI-!*eCx31215LGlfnfeC{-v%h zq!wG`#RZ>!Q4Z5r7H;|l!dQ0fUgg~gKL@}=O(qM)Udiyuxl{X+tf|cgi>@&_qSu+B z2A>~GbX?aD41C2d89cAf&V3fN0-_hH50UF8bFrbaS?g&GmyxOmi91^_fdNmK5-dSAY1{V{g zu7vK7a%si#0zTQgo=PiT1H#*ec1a+(_9-DSsm=C2f58Okqns_@vNpNi#6+&MUIuZ> z$gcdG6ZB#cl*3nz!adQ2RNHN9YNA(^FX35`4c+v&wxDjp+TB;Gt3Nk~BVjkd_}FS% zg4e!y1-9}2lZh-WQ!i8r2X6pzJ&MqN50^K6xi@D*26!1wg7ZKG8dQj;_x0fFMo$ceO4viZTXB z$%8Ay@sYN*Fd*;-PcZoWQ81o`|94kHpW!fB4o?pV@eaUc;T6ayLZs+@pm9CnG2kh( zEfTv2_iZT<&%(>F%y9~V<-_5CRcK4A0(oyh;`9Wfhar3lhqStqGO7kg*@s~j;>D!n z6l_W7%?1W;O0m5nJl!LHyb#eV0qN3qz$F8pp-2zKA)Un}6&z8O%&r8B#PQ*%HFi>W z0I@m??qWA7fI8q3&y9Tn4SDbU%!2r5xeH<96Mq#_3JHjIebjGlS9T6d>@o+~uyp$n z;m@`w8sJcDG7K&!X<8cQ^`UA9?3urvV>Gq#t*^&ngtr}@_mx1x3U&NR8ACA4QwSL* z%Y-|-aucK?M10$dU0xKjXi2y{#Lm}#ry4{mstRczCVl@Txgt3E!)&s|zU%irujEqF zo3>yT$=i z4x)g&WcI>h`2zPR*9n%DWX%Bt{tk%lkUKBYJlfX$_E7{++688SqK2vIay{T&i~&|+ zT1@60HyD9<3wcueL*+siHav1y2Z)Lu#0#Iyhla<^8iIwBo@;bRuK;{WVSbm4APArG zrTU#)x8NQxL`c+~GhN9@2Z2#RNdZ9-h<1)tVwRy>7I$J6n>CANH$k~T7EX`k#5Tm| zhK4BvCWeO+3fIln>(AjXeDMC9MHhl!;hJemrcXx3Bp!lz>vu<40q7=}gRjHTV6C69 zc4yt;?vg`#92!ctD9)vAFn6APV(AmIyRziOf zN!{wsi?Fron0`DJ?L;(hXejX(nUS0zGSkRDB&bq2>y29zuyfm256cVfP<}GCVK)O zbc2nHbjOtNw*3K;A?hF31rak!dG$DkJNOO$fD;1EbAo$|Mg-fmbz;FB%{q(~1zIv# znip#?E4B5p_QA&e;DU1w2W8E{P|cJ?Ej)B#@ZGG>##$8_x(3EK1SJUA39vHR0%*!w zC%(s4vImGk5TxxH!m>q~owz4#Txc4Z7>cOPi#Wu!aUv0H*kt&JSyh)K8J!v}@MuC& z7-TqAZD2HCP!ZekB@2AIIzKoAVx03SnEyq8BjY~4WIavv3~p6-UPf=>i534dZ78o3 zA2h4N@`wTZE*^B|5hx5&aV{PS5NjZ4vlec?R2Zmmc!f0ut?EBMTaL(?rQpF+dbIh*hf$2<6_daJ;Nuj_YUZ~XgKvZ)J%-tbw| zwzg!=mb?Lk<%4t#o}_9=d@y7M4kS2bv3w`{y=w^6RI&%PplQk(6rk9tSvoidF))K5 z$)3tteVMK|;`&0^2W`+;vilVJIf{AkoWkt(-kXPnckbO0?JsoLDlC1cueRvDnxM6o$r zmobNW*!dl{+XI~Gp$_T6Lap;!j#Mt zHdy@<%(MmtVCXLGVWVNM0`{7tAjY+>_`A?KePc4`P{lil`#V6Jh9Plx9qzu#Yon>U zN=imDEpn~tgCpa)=7EYW?jr?5rVEB#)D37ziC~|#e20maJuVG5h^4$m8NbJ*G7Q}8 z--zvIB2g^-9cw&PAK2hdP3Afzy`of>YK*&TOd%_x-_d%hSM|9=9>XG;=)sE;fY7mt zZJd#AmB$-BHS_lpin06zdyw|L`)4(CfUkAiz5`}%XzP4dI5K15kgbo5B^dex8-UEax(tGc{ z7X_t+-XRFmL3*!BRYY1K6p^m<-V_l*K>Z0m+}C}tr@m{wf53Imnh$$s)_mCKtTlUP zpXWY~;g5;RT&R}=34^A#GB%K6oC#gYpa^U6`;>^Asq}I-nc6q0W;Q7zlwcH8 z)Cya)KQ^S495WNQm~6Hv+8jYATXdCMl2lGAYTGyR9TNUq0u)LJ1!wVJ+cdO0(5-E; zb(F*u%*e%A8m`3SLt`=GtZ1_%+YGbsbXD!!)n4D#x!Ki&?-?lU8QJWaMC|Q5vJURt zIpKQlHAeTU^cF4wi)n?_n!%h=knvK8OZpzg&|seyQTQf^Vb8oBmiBIFn-ocMGo%L% zu>pDO6miRdN&BHsZ1!CvV2NX2tZ0YtPM_=CJY;WOt}Jpu1XO1z&v`o>&;^5zr(on?&S`$oE2P43rDwmdXda zhkq&>dZHgu9%Ci6m1;$QO8m-BWd1^r)J{c;i{$IRKa&#CV+3TZb-$q*7S{Kfm+H$? zBy38L8jamwMS({A08?0kW_GY=GbDbU-V_DGweBxq2s#J8K0)3bo|tnK^Vy#z9iA#g1HEQXJAL)wdE@kBz16+vEo1jZ;39+S*hcGmY~ z%(ST}h9&lOI;?~V$+W z=>gM(4@Nhkq*xLMUDnMY2#X)Uz4-@e|BrisO9$JZH6I{#Etkv)m>UYRf~5{nyjpIi zW_<$@S#txIUf!O+RVykNPxSa5>x$>FVM04${|u zr(2iKJpw9QdPcbQy;t$Kdo$Fv{5SJGm?Hv=$9`|tC2A}Ae8_g?A}nrUBh4ujky_G931ZeA~Q6Bl1#+0JBxU-f4TD>h{1XfDOG+Dp&%WH&GDQe zG{tb;tc@IBFeEYRwlJpl)Z-}R;LB&)4NBZYJ)kemy`C<1gLivxGFJws_2g;TploVI zX5Sq~(pVKoYR@Wnvn{@?%Wt|;Qz>-$%-{6p!#>K<2C3-mp^UbU9A75}%epPvu{jyH z^mQ=5{9+o@WnQ4#uxNM3VO={uHATAX^ULuf#gFl(UzFZ{hAEF-NFs-n&kRr`Y&L&F z&i39XyeKNE{Jk@`a+fG}poSC9GddYvxwCsG{M#ZA7`0OUzw> zh#J#MYe2@`7K&wMMmGnTBfV2z=oY430uc?hFX0CXcdXt`#x+@4>6U#qiyowp80U9m z7q~M++8VQniEllR=X$BZnSnN_xZtyVS;>?1_z40iEk?MWY?OdxldE2_xf3QH4SxD#f^>WVt#Onx@DGCn3#sQ zi0tt!;IWazWgVurk;Iqt=CUu3MAPGq>6X&`!u8D)R!h}NW}~Y*Ej(M~C(XSw<`>Jk z7mgp5X>H^`vJCv7Drp`3(Yed|@kX4aP3S>Y*X;^;oTP2|)n1ovVbyC_ngZo3#t zH7WZz2A6L81h#l7ha}$WZike6Bi$PEKkY~3VoUqE3y_bK50h;MHD}X9`c&M&o_bSx z&`=#cypx~ua}5YmbE@0*vE+>lFFN*YnwO=BZP3RB7Z94joBRngq8|H$oSnV8c#$u~ zrcvdPB3!WQps-@%O-#w4#H__~hW7v|OLF}waY;aU_0x6hZT%b{LH={=Z zasYDm`nGvltori(&|6QX==dKfS2b3K-_Pt1{W>|@YqX5Y!tABl(cG}JranYhaD1Ru1~4IAJYNdK^_ zxV@vuZTV~N8V4lGRA&y~cIU)X_vNZn41ep9HhlP(|AM}c^8PvkS?zCq6=H%Uf`6m* zdr_@rEcG?6?&%9~e$`F=e#h0dW7Kb{b%KDhZoxx`r^k5a-+F-R$Wl&q+cR3wqs{JM z3_r^fJD#9(8XWN_1}-U!su9D9$|fGO?cPtc>9-$<9Cq~a3rv`X1<21RRfc|0ap+(n z?Ku=jMnj^a??vZfEhj68WHI!zE-?9!RwcF{r)uBIA5*rqJtUe}0b$4ev2U(Fc)FtT zLbuZrR^5)FuN{k&>O*jX!WnKzZ9H{2snVvV2s&CX;c69-7D&YNBl5@OUL>{bE1 zU#cvue9+zbbsPSl$hCSaJ0e$F&{QWMU)-7U@$8&v%UPJWxuep{-7of8Aycg$1gJ9Th2;a^iC z+M0-U8o2itqaM{s*6j-CD%dD|q1^6{-Edc8_=$16AA0|N|DiL+I;M2zG9l$j5hCyj z!gZ@MWYieW1^(!^PtYqtXhCv2o0e6pa2ATar@g~Mm847;>8|~(#G{14*hS@!H2x}$ zKk%lP=ew{BE|{k>BI;+gl3(VV*jD>H_5q`RJ?`{;wXR22{2rC-WuFHYWk=a3C+rVn z(&*E@Ufh3Q0v)0yR5}qO&L2_*hAvLCC=VsI|2a^OV8E}-b3OS1P}1BhsL!IH<6uqW zlPAl+!R9JEvWUoA(tb^x{p>z)=MZCQvr&gdqBx1eY;@p%c%8lqxh!dy_t&wRVc`rM60K_8vDW10z>e!gdrcA59ZyRSbNPkwFk1rsz) zwDWPklc~(*gz}uTJhl8y&ELiEjkogv6YEBBnruT(Y;hqORL2{HltSz!FdkQcEATdhz~0)zonaV}!rn(M&*Gc8 zbpFt@!~Rd7ODO|?bi)4fsr#Bwz}5v~6VJr`!=E4LwR(^@ybcC^xs!ywdy3RvJZiwS zHS)8_WHT5)^c3D^Qa`pfGJHY)TzbF@E5PvF^Wn{?JVgi$X{8cXX{`a>@k+ODH49|? zJTKS1_33-W^AN+PdLShsG2Hw)#)3QF^Sn_%E+}Z6ihci?IBO$=p%~{z94K;at1ULD zJ+QpY{R7cK&)}XzkH&gXW0dp#CqhA9x4!x;y|Q|s_W1R}Tc1ZaT`Ozf z;ve_!$@@O}xUzmV^0@Eft#8ED%En)kkbdHl=I$^jfpiZtOkdThm^GUIZorH~UR8^q zP{J0&2Q%EBwO@eJiPSZr+Np4JH(iTts@SOI0@~V}*R?7mOQlQ5#-1y7#(0t1x?2mkT`up4AKJS*>|u2}hVQ^)*DFJ9u2 z-*ay~;n^&oM#+!ZQU(|=^M!sHGUx2#GFF_$G z`#u&&{^=Ec@N2GX@}lfpFb`Ga)`Droc}+(6=a^fr;gc)DqB4XWWarX^X+#w12ar3A z=85U^r`jv}X3KK_lK5Pveas>X3qYVF|<;s`uV=MF$3R#iu>IP>q81G!voXI z?^sxrV7&tjf0US6WL;H&x3?23B?Do!#G0T#E10EzlsSrOGxH|S150pz-j@UDIRIof z^bc0xG2PwCo_Pejuyn9oPnRgkj@_WZ?pqo`r8dtuegDW@BIRUzRb+~!cfiWPVJSJZ z0{l-8`I77_M`;f2fqc$kWun1>p`Oe+TYoY`Hpkb(_14H~)OD=8)E-ce`T-cC^s7c|=D zPtQBU5g8|qup*5hS>;X@5Oh^7^9x!Ek|IH+9xay?07(iYqB8Ey(pyEDYJ)@(BtBT; zRI%QZ!Xf`joK>TGa^?P$&@ZnfLDZ8^!ctRQVhUb2lMn9r@9@Oot_9Rv3ZtBne9`A!rNAAvO575IpZm z!dp3t1>Fsn8uI5Fh^y>5ph4Fi%j7@Bhgnh3vGm3=4<=^!L~3ilog1woVz^^8Oqi_@ z57wwi)qZ>)ZLZflpHlQbOD<+}Bx@z+Mlq?kgArZwMfpHuv=OBam4Y4k#ZSp9DDuV2 zQ*p@i#a~qn@4$#6PSe8$6>ZTF!$V(U$bea zJT75%r0UKP$zX`Hrj{YGv1<&W zEm+c|Y|_zweADaonHQaL)8z2vlyOHM-<{43xA*^KPxcQlSnlbY6O1rhY1tKi{XSzVW4Mpx^eZJKwHL?3*``;RMitXV(CdX4x5k4IUVc&p~w*pY&Y zU;oTjI+YtQ_0s-cs7ZS>InZl`4i?w@A*=oPVcwS>j^H%3!ynU%Vbzo`nvNF_+=(a0 z9c+V2G9iy+0LJs#Q&@I}@AKhAE}SFtOc?k-gX_ng1deV@8q>YZ0swOHo$ zafK>t2 zqV-qu=@voPD{s>n_&&9<5u1fGTMooAkXR%@t!9ReSMkqR$EsGxJ60!ftCI_>@Ap=x zE>=GfTTjy&8$j1?W~&5E#nvb$!BkeXnVCKX@I_Z557OJ?yX=3AXyOu>N&#{p4c(8}Y^| z-NtwBjWh9$bJdL>Li|sRCoxqyu9f=q+I{^(jF%mY+z*Vi-)z{(Ew)-~{r)->!QFd< zTYM>RnP|Uq{jkeNf75=!_UL%2pknfpHDmC>IG~!b@LJD1$|^~A@nCYXKFf?y!OHyk z0F2(Crs>1wo*gyZytm3SyhF!V{J|ZHkKcz)->ez?zumI_I*^1mP36Z^vLS{ zFjg-O30k-{-I!8CHj9q!KL@P{tPkALUKAtSPT|*R+3NzQC%)t~h4*bo(kW*-FQbFZ z#81Xpa<)zy#}e1Hn^YZQZeQh{LdZT@28oZo;u-@fc2EWs|* zYI(BzI{@dUnqa4Rukp%jbGrShi1rUmi`&J{OG30G7-L7Z%QrI3wl%n&o>|A6wB5gH z_<~9^i?2$yLC%p2OGVC;0@a&Bi(@92+lfcp#x8rB)lRL2c8orzyf?e2MUHX9dkhM@ zS!r8O!zbH-E)F)ichvSCO-#~9IJ+6`r@S3iE}7)Nv3EqPv7GGC{@4kWa7u2sr8U_I zRiz}nnX!%lB2;7NsX+)dKg9tmuwz zXqu!*c#ft?q-Gp5fJP!3aR-lBb&7NHPoK2s6 zNzWaF`^v~KZ||Rc)$z59aysukxw@P+r}H)P_x&C3BiQF_pXqx&GX00-G|KTCfu!ya ziBlreQ)1UslEJAcStP|f4{3&%fMO|*m=)f(%1c& z)B_+dEIAprcs8b4as$lIzY9o8f=ADU4$g$H&hC+(BN@&`c+c-k2ELLH6z;S)e0wJL z!7w8DnEFv5yTlZ?V!CvOt#oIg!00(`3*@MkPT|}6BlmIngfk^w`ZDd4P`V&A=~VQ#OZWO@ z1)DYaD-3vZ&3V#oxndnSpE$F7dLZn*5ayn2dhTowJmBi?aI_~}$qO#y4Hxx+-wl9s z2g5l-;OrrAj@yP0oXZK$Wd-LkhI8K*)1u*Aif~S8IENS)d zjf0DgT>ybVvLi$}*`(Ov@(8#J8(aep*Mh@!;Bb95xG@{t6alwlXS3u)Sn;r%@^Kj7 z<6^V8WPW`nXE=$b;Mw-GWj5!U^4?*@s2*IzqZ~1q@%Flw8(}I=r!j+%@#pr6?M7iuWG7|Zv6%E}ZX~?9BaV*270G5vi z@&9XWW#w2F-+zE+sLPa7_{Nn4`KYv2v-x&|1wx=NSf79P`GrVySbtY>9dTh7Y7qA( zP5VFnNyb>=dm}~s9m~Dp8j<9yi@JhZk2~Wj1Pj0zu;*2*%?19y42N`hFBZC{`!yC5 z2cFCFuN)`p(G4bcYi)lIkHvq6+!_w^T3lo$npk=q^oex9U9>0@mo2ngARiCuOh1rW zK@d+^c;FBzQvYRi*g}y5xW>wQbFT^Ib(OyTgbcI%vBLYs5@$q^>)t>p0BcN7+1SEL z*l@F;Qczv)>$LVaoPn`|xvxTtCh)4y5qS|AYJh)r;kfxaLfMJu)}JJt{oYly8~adX zGtFe#eJ3%Cm|`M32IpzjL|llv8)TSDE0v8&rC{`am2q$N)D&i(NPvB)=)Q{_log<_ zy8UJpQJa_Z=DwVh_WkOQV*)t-*#DsA>6~sxpJ5D|Xc&RSM1B}jjG=xjDJ}6#i z58UG7p_PPkZy`=;3eV&Xb$OeaDt9a#1wt}}=?M5WRcC{#V`RHd5!eVTibYM;o0{s+ z5cv!UdXakhT2uf~mAtfrdZmoxC>*Bu{20x3X+A|C_{^k6*__o>`{lUHA!8{%d3aD( zeQ7qfDUICeD0~Trr6_;r)pl6zK=JVdS3boX#{$BpEc!>luk>i~1nQc+USi{YHjIc{ zduf8rpVlTk!Ep2 z3Py2_9rIzc&1pgjV_Q#e9I$vZO+*xc;OJn`tWlHrH7yHO(4r>dLQ374AB^laA>(}E z@9V86VViThp&O(0448G@OMrs>eHh*I1>81A`ReXj#fQu%w}!)EicH`XeLl(Oe39t8 zPJ@3T}OA*7G~zD04{{e9@eP`7ih7l>o752INiA#B<5Q97!1B-jA{G8^_%FlZ&Hu0A&}_1tz6*~Nexi?> z+15OV^HPb_RuXMFTG)((aF9|$7|$;pU%DRFsg{F`Ad8gj9X=D*T29GBRx$6UOo6QA zB3ZW6Gj6s|-H5Bf8ZQ9^Zc3@`Co>T&W;ni(!xUpsG$&jS$4j=YCY6Iu4`R^(xez3O zg$^6gPclFX&$6Qtr`9t=lVYE#9%OZwW`*%3A%G;cwNgXMCS_YzT6NS$)K0(#sW>(O zGTJ!g8D~Dr9x5+%{6WAWebrCI)0ZNwl@N9*3)!Skr#Mu2_xt;jTw~-u_T>J_mG=&B zn0JlhndqEt;tKiv_a)TJz-T}zV|gtxXKUy~1D*Tn>qPyMRs;?4o9WQp1pk5 zD9T1Z&qyLdqx%d2q)+O{59wsxInEdHXk+5^_BziGvq578f3BcjcH~cm@rV)TN9hjP zz!VlmKHQ`(Ma-@2PxP7)!&rSfMDY?~4|jQm zU^2r+KSrb`MBCAqYbZOK+D}uyh#n9P@e{}*NI}u=S{3Pyt_c?EEEE#}EPm%@e_}ua z;I{}He&&!)g4B1~L-0ZJS!{9;7j1t9M8<8v=U6X09Oc0J_b2g?QiIc3s%f3|@-9o}l2KC<0AJF?ZrWY0FBICNxm zV4hU3l30ZBKb>$!~f!#hDS=Fr%dFDU7 z{HlawGd|-U@)+|A=JGhpU49QLCT@H3AjRVMf^gLWGqq|(?hKa$VIb)S5l3HtKSNCkafYY>u~`mNbRnr z&>5jlh9kHo!ih}7;6zg`tMfSrMu^4^UD1m7aVSZLJ2rxV_4B`LO7E$5Gn zkcZjRYylh0_y9I~VBZp>efC$R`2=|{JN;vc3(xf5?s^{&cuT8@4hMT1)aVOtM}n!8 zt#>rKTwxI=`e1gcSVt+3_OO5ta$fdD!+^MO*c zG!fV<3G)6&_DNTo@K$mf7vNKI(3=>r1*4WFjy4K1bLq>csMM%1T2Rja$Mrw>7;! zbk0y7;3xmSLS8o5XeUdJLPR0QQ9;6)YAA?s4P|WS2NnM5R(nYJagzzH3KPaawVadJ zf$+{lrfomS7FhXYD1U3v*XeZuj5Mew}Ew36rvp~dd2ph`Mjd=y{5 zo@kwGBJU=Joj(|FM;Y3RQ9%kqc2Tl{&>t#R=R-j%{z<^hr(r)bIYe|Xj9^Lw@rWMS zIjs(F&og)?q>+e-XyAsTZ1fq?cS18dZL$0;27ik9%B<3Pa;c*%W$El&d6HUawnl#T ztvpG4DO=}P_N_e0;QzCfmnp|VI>*U8$0an!EkDPjC&z0k$LCj$A5(6CbZ(G&?xWD$ zko?>yJ-J~^xe>o|F-&>U(s{AwdGVoniTQb2=Fs@syoe(uqP7RX?(F+0NIQU>hyXZ< z0>8qLW3hsFkl^xA@?>m6x;s^)YUVRS=ZEeH<51hvjld^-nXeE-cgS4YGi82f8 z0U0$%R6^4frY|YVObyh?fCHtZf{4N}Qb=ik{5=3@PmmQXndbM03WVC3 zH4{}KpG%?SWvl}gQdk1TJC(pFf@Af{qZshSDaG}sRi>HDER3P%aW-EvkVOw%+8R!* z4|HgKQB77_Sj93q0AuLUpPErj3w<_-D2PZVf2vm!@ww(^ZoYIz=h6)o45{H`m#Q(yKP(u^T1;Dbq&LV7pJt2~J0hil6V z+Y333&q;vgT!#6+e9@RACvvBByb|E4g_`o?53lFX(R3zb~wN_;R|@gBS% z2QL?Asdcvaom^4xEd9G%Ma&UAhJ?R1rV_@skU0@cAy@~HaCcOBM=sVb>Hz@3GG8VR ztW&wi&N_~>np(&a>IWYSz&?-H^Lc9V5IHGd-pH~pxR}4>=6dPWvwK&a!Qh)8*wDl| zFxr`}OyQe@aI!x19g=+lL0QMairlU$F1=To05&K^`~r~qs<bY61wkZJR7WbaX z)NG3jW%)c)=C^ptxh3$DYo&Ra94(!oDEBzd_&^xN8-#WZB8oz3)<6JEih$D=Vt>M~ zb{2OQ?xvPmtkh*9&b3IMT3A>%rQYoXDs?J4h34f}6c%<74}_L*ZoTB^v%%K_1m3-PNQ6;+A z3L+8gwH|gvzmHG}N|FLz>+gU61^D-K&$Wuh@ppxb^Pt9*fi^8V?$BWu{w%H4VVyt2 zdMr2tIh>Id&Lj+HR)n+Y#c5VTTl)(Iwg?OKJc;g;r2|H+8hLC|NBG!BoLGPbuu=c8 z5gr0yJ|+@wKjKw10(?0l_?_^kKo9h&mduH9qyRI@8by*0C<@a?MD$YoS>cbB@ORlq z5+|vgH$mgecmsXl2m%ZxC%42C_Dz>I+G#Dfl4k+NTQLoU-wC=mOHmfonN~0{JF-MQ zvH}Fi899RTn~3ux@lOSX%ufW!jjAJnj*awENU}sgb5R)G#;LmOCP9ZD`L&4d{4qfZ zO5Fwm#-hrakq;Z3$Tg8b8~~h-n#k6Mb%l*OW65HtiBrNR9K)u%d-ZdyVEGU)q#u4J zjDa5oJ|hKWk!oz5;z+h|6}!Ozy}*7GpjpQl8^P#k<5*WCNF6&C0U&e@n@GfJv9;m} z+dhc*Ob}Atkffky2;}L;`^R;(G~Z#!X|M4V*WfBW^EJCbF8zs<)CmA~Onq}E5-?(00LkJxVB4JF8kK@POigGW?LE)kM5YOyCf=Hf zm?lg=eWCx5jt>bgYlUr{s)NH9D4;Xn#pn7(sBMK{_Dy4s(0Rg4VsGokL^)tC3gnCe z`N}hhBOsfN6%ETD`_=dzYZ^#~T4;J)f!Qg+^<=^&HZc0~$dXtcL-X3|}gD z3nl`|c&wPB7ummm5J*`wuUune2j*fx2S;lg4?n@#N8W_h@Bd$=yx|+7d(R_S$AZ&Bc_{d3GE zs&g!9>^`b6n{Aq@O}-LU-o96l#^nEyS4^xwVyca8uk761ehA&`K9C}PSx+IU#mP#h(lmKUgPdpMDdz-{$^WibqP zFlmH3gfMIX_XL5UVV_lJ#*52as|09b*CxA<#Z}Gt4 z#XU_M?LZytgFckH^M cduZ?EjCkxGstandards = self::findConfiguration($path); - - // Autoload class to set up a bunch of PHP_CodeSniffer-specific token type constants - spl_autoload_call(Tokens::class); - - $file = new DummyFile($content, new Ruleset($config), $config); - $file->process(); - $fixed = $file->fixer->fixFile(); - if (!$fixed && $file->getErrorCount() > 0) { - throw new Exception('Unable to format file'); - } - - $new = $file->fixer->getContents(); - if ($content === $new) { - return []; - } - return [new TextEdit(new Range(new Position(0, 0), self::calculateEndPosition($content)), $new)]; - } - - /** - * Calculate position of last character. - * - * @param string $content document as string - * - * @return \LanguageServer\Protocol\Position - */ - private static function calculateEndPosition(string $content): Position - { - $lines = explode("\n", $content); - return new Position(count($lines) - 1, strlen(end($lines))); - } - - /** - * Search for PHP_CodeSniffer configuration file at given directory or its parents. - * If no configuration found then PSR2 standard is loaded by default. - * - * @param string $path path to file or directory - * @return string[] - */ - private static function findConfiguration(string $path) - { - if (is_dir($path)) { - $currentDir = $path; - } else { - $currentDir = dirname($path); - } - do { - $default = $currentDir . DIRECTORY_SEPARATOR . 'phpcs.xml'; - if (is_file($default)) { - return [$default]; - } - - $default = $currentDir . DIRECTORY_SEPARATOR . 'phpcs.xml.dist'; - if (is_file($default)) { - return [$default]; - } - - $lastDir = $currentDir; - $currentDir = dirname($currentDir); - } while ($currentDir !== '.' && $currentDir !== $lastDir); - - $standard = Config::getConfigData('default_standard') ?? 'PSR2'; - return explode(',', $standard); - } -} diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 118dd93..73560f4 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -265,8 +265,6 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher $serverCapabilities->documentSymbolProvider = true; // Support "Find all symbols in workspace" $serverCapabilities->workspaceSymbolProvider = true; - // Support "Format Code" - $serverCapabilities->documentFormattingProvider = true; // Support "Go to definition" $serverCapabilities->definitionProvider = true; // Support "Find all references" diff --git a/src/PhpDocument.php b/src/PhpDocument.php index ff3cf8b..0ac8421 100644 --- a/src/PhpDocument.php +++ b/src/PhpDocument.php @@ -166,19 +166,6 @@ class PhpDocument $this->sourceFileNode = $treeAnalyzer->getSourceFileNode(); } - /** - * Returns array of TextEdit changes to format this document. - * - * @return \LanguageServer\Protocol\TextEdit[] - */ - public function getFormattedText() - { - if (empty($this->getContent())) { - return []; - } - return Formatter::format($this->getContent(), $this->uri); - } - /** * Returns this document's text content. * diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index 2bcda2c..58e7074 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -159,20 +159,6 @@ class TextDocument $this->documentLoader->close($textDocument->uri); } - /** - * The document formatting request is sent from the server to the client to format a whole document. - * - * @param TextDocumentIdentifier $textDocument The document to format - * @param FormattingOptions $options The format options - * @return Promise - */ - public function formatting(TextDocumentIdentifier $textDocument, FormattingOptions $options) - { - return $this->documentLoader->getOrLoad($textDocument->uri)->then(function (PhpDocument $document) { - return $document->getFormattedText(); - }); - } - /** * The references request is sent from the client to the server to resolve project-wide references for the symbol * denoted by the given text document position. diff --git a/tests/FormatterTest.php b/tests/FormatterTest.php deleted file mode 100644 index a46f2ec..0000000 --- a/tests/FormatterTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertSame($output, $edits[0]->newText); - } - - public function testFormatNoChange() - { - $expected = file_get_contents(__DIR__ . '/../fixtures/format_expected.php'); - - $edits = Formatter::format($expected, 'file:///whatever'); - $this->assertSame([], $edits); - } -} diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 5d03451..5d58172 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -34,7 +34,6 @@ class LanguageServerTest extends TestCase $serverCapabilities->textDocumentSync = TextDocumentSyncKind::FULL; $serverCapabilities->documentSymbolProvider = true; $serverCapabilities->workspaceSymbolProvider = true; - $serverCapabilities->documentFormattingProvider = true; $serverCapabilities->definitionProvider = true; $serverCapabilities->referencesProvider = true; $serverCapabilities->hoverProvider = true; diff --git a/tests/Server/TextDocument/FormattingTest.php b/tests/Server/TextDocument/FormattingTest.php deleted file mode 100644 index 1844891..0000000 --- a/tests/Server/TextDocument/FormattingTest.php +++ /dev/null @@ -1,50 +0,0 @@ -uri = $uri; - $textDocumentItem->languageId = 'php'; - $textDocumentItem->version = 1; - $textDocumentItem->text = file_get_contents($path); - $textDocument->didOpen($textDocumentItem); - - // how code should look after formatting - $expected = file_get_contents(__DIR__ . '/../../../fixtures/format_expected.php'); - // Request formatting - $result = $textDocument->formatting(new TextDocumentIdentifier($uri), new FormattingOptions())->wait(); - $this->assertEquals([new TextEdit(new Range(new Position(0, 0), new Position(20, 0)), $expected)], $result); - } -} From db484617b638a8af2f617b4fe61aeb1af7f1bd9c Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 29 Oct 2017 13:18:25 -0700 Subject: [PATCH 29/55] ci: speed up submodule cloning --- .gitmodules | 9 +++++++++ .travis.yml | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/.gitmodules b/.gitmodules index 06d05cb..b26d58d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,27 +1,36 @@ [submodule "validation/frameworks/php-language-server"] path = validation/frameworks/php-language-server url = https://github.com/felixfbecker/php-language-server + shallow = true [submodule "validation/frameworks/wordpress"] path = validation/frameworks/wordpress url = https://github.com/wordpress/wordpress + shallow = true [submodule "validation/frameworks/drupal"] path = validation/frameworks/drupal url = https://github.com/drupal/drupal + shallow = true [submodule "validation/frameworks/tolerant-php-parser"] path = validation/frameworks/tolerant-php-parser url = https://github.com/microsoft/tolerant-php-parser + shallow = true [submodule "validation/frameworks/symfony"] path = validation/frameworks/symfony url = https://github.com/symfony/symfony + shallow = true [submodule "validation/frameworks/math-php"] path = validation/frameworks/math-php url = https://github.com/markrogoyski/math-php + shallow = true [submodule "validation/frameworks/codeigniter"] path = validation/frameworks/codeigniter url = https://github.com/bcit-ci/codeigniter + shallow = true [submodule "validation/frameworks/cakephp"] path = validation/frameworks/cakephp url = https://github.com/cakephp/cakephp + shallow = true [submodule "validation/frameworks/phpunit"] path = validation/frameworks/phpunit url = https://github.com/sebastianbergmann/phpunit + shallow = true diff --git a/.travis.yml b/.travis.yml index ac0aec5..5593c66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,9 @@ php: - 7.1 - 7.2 +git: + submodules: false + services: - docker @@ -18,6 +21,7 @@ cache: - $HOME/.npm install: + - git submodule update --init --jobs 9 - composer install --prefer-dist --no-interaction script: From 235a790156d69079927f73195d043f6bba96d9a3 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 5 Nov 2017 01:51:33 -0800 Subject: [PATCH 30/55] ci: remove shallow submodule cloning --- .gitmodules | 9 --------- .travis.yml | 1 + 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.gitmodules b/.gitmodules index b26d58d..06d05cb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,36 +1,27 @@ [submodule "validation/frameworks/php-language-server"] path = validation/frameworks/php-language-server url = https://github.com/felixfbecker/php-language-server - shallow = true [submodule "validation/frameworks/wordpress"] path = validation/frameworks/wordpress url = https://github.com/wordpress/wordpress - shallow = true [submodule "validation/frameworks/drupal"] path = validation/frameworks/drupal url = https://github.com/drupal/drupal - shallow = true [submodule "validation/frameworks/tolerant-php-parser"] path = validation/frameworks/tolerant-php-parser url = https://github.com/microsoft/tolerant-php-parser - shallow = true [submodule "validation/frameworks/symfony"] path = validation/frameworks/symfony url = https://github.com/symfony/symfony - shallow = true [submodule "validation/frameworks/math-php"] path = validation/frameworks/math-php url = https://github.com/markrogoyski/math-php - shallow = true [submodule "validation/frameworks/codeigniter"] path = validation/frameworks/codeigniter url = https://github.com/bcit-ci/codeigniter - shallow = true [submodule "validation/frameworks/cakephp"] path = validation/frameworks/cakephp url = https://github.com/cakephp/cakephp - shallow = true [submodule "validation/frameworks/phpunit"] path = validation/frameworks/phpunit url = https://github.com/sebastianbergmann/phpunit - shallow = true diff --git a/.travis.yml b/.travis.yml index 5593c66..ffeef4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ php: - 7.2 git: + depth: 10 submodules: false services: From 74578c7b583248231736da7791441fc376516a54 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 5 Nov 2017 02:28:38 -0800 Subject: [PATCH 31/55] ci(travis): only test lowest and highest PHP version --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ffeef4a..289f969 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: php php: - 7.0 - - 7.1 - 7.2 git: From 41e84880b380368ae5862c01e9a00d55067c48ec Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 5 Nov 2017 02:30:34 -0800 Subject: [PATCH 32/55] ci(travis): use string versions --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 289f969..a270acf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: php php: - - 7.0 - - 7.2 + - '7.0' + - '7.2' git: depth: 10 From b03b9a239c38db5d4551ce5b9f0e3876e458edd4 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 5 Nov 2017 02:54:56 -0800 Subject: [PATCH 33/55] ci(travis): run on OSX (#517) --- .travis.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.travis.yml b/.travis.yml index a270acf..fa0f27f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,20 @@ php: - '7.0' - '7.2' +matrix: + include: + - os: osx + osx_image: xcode9.1 + language: generic + before_install: + # Fix ruby error https://github.com/Homebrew/brew/issues/3299 + - brew update + - brew tap homebrew/homebrew-php + - brew install php71 + - brew install homebrew/php/php71-xdebug + - curl https://getcomposer.org/installer | php + - ln -s "`pwd`/composer.phar" /usr/local/bin/composer + git: depth: 10 submodules: false From f5c45f83edb2bf73b90cff2f35fd252d391ed278 Mon Sep 17 00:00:00 2001 From: Brandon Max Date: Thu, 9 Nov 2017 19:15:36 -0500 Subject: [PATCH 34/55] docs(contributing): document how to use XDebug (#518) --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 8e7117c..b25680e 100644 --- a/README.md +++ b/README.md @@ -201,3 +201,9 @@ Lint with The project parses PHPStorm's PHP stubs to get support for PHP builtins. It re-parses them as needed after Composer processes, but after some code changes (such as ones involving the index or parsing) you may have to explicitly re-parse them: composer run-script parse-stubs + +To debug with xDebug ensure that you have this set as an environment variable + + COMPOSER_ALLOW_XDEBUG=1 + +This tells the Language Server to not restart without XDebug if it detects that XDebug is enabled (XDebug has a high performance impact). From b4a3134e2a74b008bcc363a8e9d2fd16692c799a Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 9 Nov 2017 16:57:05 -0800 Subject: [PATCH 35/55] ci(travis): use build stages --- .travis.yml | 59 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/.travis.yml b/.travis.yml index fa0f27f..e99a2cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,30 +1,14 @@ + language: php php: - '7.0' - '7.2' -matrix: - include: - - os: osx - osx_image: xcode9.1 - language: generic - before_install: - # Fix ruby error https://github.com/Homebrew/brew/issues/3299 - - brew update - - brew tap homebrew/homebrew-php - - brew install php71 - - brew install homebrew/php/php71-xdebug - - curl https://getcomposer.org/installer | php - - ln -s "`pwd`/composer.phar" /usr/local/bin/composer - git: depth: 10 submodules: false -services: - - docker - env: global: - BUILD_LEADER_ID=1 @@ -37,18 +21,43 @@ cache: install: - git submodule update --init --jobs 9 - composer install --prefer-dist --no-interaction - script: - vendor/bin/phpcs -n - vendor/bin/phpunit --coverage-clover=coverage.xml - -after_success: +after_script: - bash <(curl -s https://codecov.io/bash) - - git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/* - - git fetch --tags - - nvm install 8 && nvm use 8 - - npm install - - npm run semantic-release + +jobs: + include: + - stage: test + os: osx + osx_image: xcode9.1 + language: generic + before_install: + # Fix ruby error https://github.com/Homebrew/brew/issues/3299 + - brew update + - brew tap homebrew/homebrew-php + - brew install php71 + - brew install homebrew/php/php71-xdebug + - curl https://getcomposer.org/installer | php + - ln -s "`pwd`/composer.phar" /usr/local/bin/composer + - stage: release + language: node_js + node_js: '8' + services: + - docker + before_install: + - git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/* + - git fetch --tags + install: + - npm install + script: + - npm run semantic-release + +stages: + - test + - name: release + if: branch = master branches: except: From 857fe26eb51ce174d90390fde8c68fc702c3e033 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 9 Nov 2017 18:48:02 -0800 Subject: [PATCH 36/55] ci(travis): optimize --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e99a2cc..3ca1f2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,16 +15,15 @@ env: cache: directories: + - $HOME/Library/Caches/Homebrew - $HOME/.composer/cache - $HOME/.npm install: - - git submodule update --init --jobs 9 - composer install --prefer-dist --no-interaction script: - vendor/bin/phpcs -n - vendor/bin/phpunit --coverage-clover=coverage.xml -after_script: - bash <(curl -s https://codecov.io/bash) jobs: @@ -51,7 +50,7 @@ jobs: - git fetch --tags install: - npm install - script: + after_success: - npm run semantic-release stages: From d54ece33669575b56021bf1c199693a7b878fd2e Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 9 Nov 2017 18:59:41 -0800 Subject: [PATCH 37/55] build(docker): optimize docker build --- Dockerfile | 5 ----- release-docker.php | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index f4291db..549a28f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,11 +7,6 @@ FROM php:7-cli MAINTAINER Felix Becker -RUN apt-get update \ - # Needed for CodeSniffer - && apt-get install -y libxml2 libxml2-dev \ - && rm -rf /var/lib/apt/lists/* - RUN docker-php-ext-configure pcntl --enable-pcntl RUN docker-php-ext-install pcntl COPY ./php.ini /usr/local/etc/php/conf.d/ diff --git a/release-docker.php b/release-docker.php index 8cac2db..f833d60 100755 --- a/release-docker.php +++ b/release-docker.php @@ -6,5 +6,7 @@ $dockerPassword = getenv('DOCKER_PASSWORD'); $version = json_decode(file_get_contents(__DIR__ . '/package.json'))->version; system("docker login -e=$dockerEmail -u=$dockerUsername -p=$dockerPassword"); -system("docker build -t felixfbecker/php-language-server:$version ."); +system("docker build -t felixfbecker/php-language-server:latest ."); +system("docker tag felixfbecker/php-language-server:latest felixfbecker/php-language-server:$version ."); system("docker push felixfbecker/php-language-server:$version"); +system("docker push felixfbecker/php-language-server:latest"); From eadf305a1f86adc79ad7e10f69d343613a1b49aa Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 9 Nov 2017 19:07:43 -0800 Subject: [PATCH 38/55] ci(travis): fix release --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3ca1f2f..54f8f26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,11 +45,12 @@ jobs: node_js: '8' services: - docker - before_install: + install: - git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/* - git fetch --tags - install: + - composer install --prefer-dist --no-interaction - npm install + script: skip after_success: - npm run semantic-release From 3e41244b6f82e3419d6335d17119ef7da7737c6e Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 9 Nov 2017 22:01:50 -0800 Subject: [PATCH 39/55] ci(travis): use PHP 7 for release --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 54f8f26..22b4766 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,13 +42,15 @@ jobs: - ln -s "`pwd`/composer.phar" /usr/local/bin/composer - stage: release language: node_js - node_js: '8' + php: '7.0' services: - docker install: - git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/* - git fetch --tags - composer install --prefer-dist --no-interaction + - nvm install 8 + - nvm use 8 - npm install script: skip after_success: From 0e645301cc306dc86050118816576ed20ed882d8 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 9 Nov 2017 22:40:56 -0800 Subject: [PATCH 40/55] ci(travis): remove language tag --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 22b4766..1fc8544 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,7 +41,6 @@ jobs: - curl https://getcomposer.org/installer | php - ln -s "`pwd`/composer.phar" /usr/local/bin/composer - stage: release - language: node_js php: '7.0' services: - docker From 9434cb1b67225c841ea32b0662280be4fb089d5d Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Fri, 10 Nov 2017 00:26:03 -0800 Subject: [PATCH 41/55] ci(release): set verifyConditions to empty array --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 75f02e1..d08d1a4 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ } }, "release": { + "verifyConditions": [], "getLastRelease": "last-release-git" }, "repository": { From 1804ac8d978a318a4a6b88dc02b4362854b6a65b Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Fri, 10 Nov 2017 01:16:15 -0800 Subject: [PATCH 42/55] ci(travis): correct BUILD_LEADER_ID --- .travis.yml | 2 +- package.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1fc8544..5496dee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ git: env: global: - - BUILD_LEADER_ID=1 + - BUILD_LEADER_ID=4 cache: directories: diff --git a/package.json b/package.json index d08d1a4..75f02e1 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,6 @@ } }, "release": { - "verifyConditions": [], "getLastRelease": "last-release-git" }, "repository": { From 0afc3320d518967b5a1aba0b9c2610c2f5b67a8f Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 12 Nov 2017 12:40:13 -0800 Subject: [PATCH 43/55] ci(travis): pin version to 7.2RC5 7.2RC6 is causing segfaults --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5496dee..4eb97ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: php php: - '7.0' - - '7.2' + - '7.2RC5' git: depth: 10 From 1ec8d8d8e20e6d4370e28743851aa6128e579280 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 12 Nov 2017 12:41:51 -0800 Subject: [PATCH 44/55] ci(travis): correct version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4eb97ee..fa4520e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: php php: - '7.0' - - '7.2RC5' + - '7.2.0RC5' git: depth: 10 From 607cd8158d01f15c0d7fe784082a66aa7fa2e25a Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 15 Nov 2017 13:08:15 -0800 Subject: [PATCH 45/55] test(index): add IndexTest --- tests/Index/IndexTest.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/Index/IndexTest.php diff --git a/tests/Index/IndexTest.php b/tests/Index/IndexTest.php new file mode 100644 index 0000000..8236f8d --- /dev/null +++ b/tests/Index/IndexTest.php @@ -0,0 +1,30 @@ +setDefinition('SomeNamespace\SomeClass', new Definition); + $methodDefinition = new Definition; + $methodFqn = 'SomeNamespace\SomeClass->someMethod()'; + $index->setDefinition($methodFqn, $methodDefinition); + $index->setDefinition('SomeNamespace\SomeClass->someProperty', new Definition); + $this->assertSame($methodDefinition, $index->getDefinition($methodFqn)); + } + + public function testGetSetClassDefinition() + { + $index = new Index; + $definition = new Definition; + $fqn = 'SomeNamespace\SomeClass'; + $index->setDefinition($fqn, $definition); + $this->assertSame($definition, $index->getDefinition($fqn)); + } +} From 06747bb734ec2b40fb1d9edc238fe7ec3fb9363d Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 15 Nov 2017 13:14:08 -0800 Subject: [PATCH 46/55] ci(travis): don't release on PRs --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fa4520e..06c4140 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ jobs: stages: - test - name: release - if: branch = master + if: branch = master AND type = push AND fork = false branches: except: From b1a1875070ea93fdcd5c70ffe7fef8ef25c9b3fb Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 15 Nov 2017 22:38:01 -0800 Subject: [PATCH 47/55] fix(completion): don't suggest characer (#527) closes #372 --- fixtures/completion/html_no_completion.php | 1 + src/CompletionProvider.php | 23 +++++++++-- src/Protocol/CompletionContext.php | 30 ++++++++++++++ src/Protocol/CompletionTriggerKind.php | 16 ++++++++ src/Server/TextDocument.php | 10 +++-- tests/Server/TextDocument/CompletionTest.php | 41 +++++++++++++++++++- 6 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 fixtures/completion/html_no_completion.php create mode 100644 src/Protocol/CompletionContext.php create mode 100644 src/Protocol/CompletionTriggerKind.php diff --git a/fixtures/completion/html_no_completion.php b/fixtures/completion/html_no_completion.php new file mode 100644 index 0000000..9318418 --- /dev/null +++ b/fixtures/completion/html_no_completion.php @@ -0,0 +1 @@ +< diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 68c5a0c..d774423 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -10,7 +10,9 @@ use LanguageServer\Protocol\{ Position, CompletionList, CompletionItem, - CompletionItemKind + CompletionItemKind, + CompletionContext, + CompletionTriggerKind }; use Microsoft\PhpParser; use Microsoft\PhpParser\Node; @@ -122,9 +124,10 @@ class CompletionProvider * * @param PhpDocument $doc The opened document * @param Position $pos The cursor position + * @param CompletionContext $context The completion context * @return CompletionList */ - public function provideCompletion(PhpDocument $doc, Position $pos): CompletionList + public function provideCompletion(PhpDocument $doc, Position $pos, CompletionContext $context = null): CompletionList { // This can be made much more performant if the tree follows specific invariants. $node = $doc->getNodeAtPosition($pos); @@ -152,7 +155,21 @@ class CompletionProvider // Inspect the type of expression under the cursor - if ($node === null || $node instanceof Node\Statement\InlineHtml || $pos == new Position(0, 0)) { + $content = $doc->getContent(); + $offset = $pos->toOffset($content); + if ( + $node === null + || ( + $node instanceof Node\Statement\InlineHtml + && ( + $context === null + // Make sure to not suggest on the > trigger character in HTML + || $context->triggerKind === CompletionTriggerKind::INVOKED + || $context->triggerCharacter === '<' + ) + ) + || $pos == new Position(0, 0) + ) { // HTML, beginning of file // Inside HTML and at the beginning of the file, propose triggerKind = $triggerKind; + $this->triggerCharacter = $triggerCharacter; + } +} diff --git a/src/Protocol/CompletionTriggerKind.php b/src/Protocol/CompletionTriggerKind.php new file mode 100644 index 0000000..d36129d --- /dev/null +++ b/src/Protocol/CompletionTriggerKind.php @@ -0,0 +1,16 @@ + */ - public function completion(TextDocumentIdentifier $textDocument, Position $position): Promise + public function completion(TextDocumentIdentifier $textDocument, Position $position, CompletionContext $context = null): Promise { - return coroutine(function () use ($textDocument, $position) { + return coroutine(function () use ($textDocument, $position, $context) { $document = yield $this->documentLoader->getOrLoad($textDocument->uri); - return $this->completionProvider->provideCompletion($document, $position); + return $this->completionProvider->provideCompletion($document, $position, $context); }); } diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index 0d68ec3..424dc3c 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -17,7 +17,9 @@ use LanguageServer\Protocol\{ Position, CompletionList, CompletionItem, - CompletionItemKind + CompletionItemKind, + CompletionContext, + CompletionTriggerKind }; use function LanguageServer\pathToUri; @@ -464,6 +466,41 @@ class CompletionTest extends TestCase ], true), $items); } + public function testHtmlPrefixShouldNotTriggerCompletion() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_no_completion.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(0, 1), + new CompletionContext(CompletionTriggerKind::TRIGGER_CHARACTER, '>') + )->wait(); + $this->assertEquals(new CompletionList([], true), $items); + } + + public function testHtmlPrefixShouldTriggerCompletionIfManuallyInvoked() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_no_completion.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(0, 1), + new CompletionContext(CompletionTriggerKind::INVOKED) + )->wait(); + $this->assertEquals(new CompletionList([ + new CompletionItem( + ' Date: Sat, 18 Nov 2017 16:59:57 -0800 Subject: [PATCH 48/55] fix(indexing): properly resolve self, static and parent keywords (#532) Previously we would dump static, self and parent as literal FQNs into the index. --- .travis.yml | 2 +- .vscode/launch.json | 52 +++++----- src/DefinitionResolver.php | 28 +++++- src/TreeAnalyzer.php | 94 ++++++++++++------- tests/Server/ServerTestCase.php | 36 +++---- .../TextDocument/Definition/GlobalTest.php | 24 +++-- .../Definition/NamespacedTest.php | 4 +- tests/Server/TextDocument/HoverTest.php | 2 +- .../TextDocument/References/GlobalTest.php | 2 +- .../WithReturnTypehints.php.expected.json | 5 +- .../cases/nameToken.php.expected.json | 6 +- .../cases/parent1.php.expected.json | 3 - .../cases/parent3.php.expected.json | 3 - .../Validation/cases/self1.php.expected.json | 2 +- .../Validation/cases/self2.php.expected.json | 2 +- .../Validation/cases/self3.php.expected.json | 2 +- .../Validation/cases/self4.php.expected.json | 2 +- .../cases/static1.php.expected.json | 2 +- .../cases/static2.php.expected.json | 2 +- .../cases/static3.php.expected.json | 4 +- .../cases/static4.php.expected.json | 3 + 21 files changed, 172 insertions(+), 108 deletions(-) diff --git a/.travis.yml b/.travis.yml index 06c4140..9b247b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ install: - composer install --prefer-dist --no-interaction script: - vendor/bin/phpcs -n - - vendor/bin/phpunit --coverage-clover=coverage.xml + - vendor/bin/phpunit --coverage-clover=coverage.xml --colors=always - bash <(curl -s https://codecov.io/bash) jobs: diff --git a/.vscode/launch.json b/.vscode/launch.json index 0f77709..1e4b234 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,29 +1,27 @@ { - "version": "0.2.0", - "configurations": [ - { - "name": "Listen for XDebug", - "type": "php", - "request": "launch", - "port": 9000 - }, - { - "name": "Launch currently open script", - "type": "php", - "request": "launch", - "program": "${file}", - "cwd": "${fileDirname}", - "port": 9000 - }, - { - "name": "PHPUnit", - "type": "php", - "request": "launch", - "program": "${workspaceRoot}/vendor/phpunit/phpunit/phpunit", - "cwd": "${workspaceRoot}", - "args": [ - // "--filter", "CompletionTest" - ] - } - ] + "version": "0.2.0", + "configurations": [ + { + "name": "PHPUnit", + "type": "php", + "request": "launch", + "program": "${workspaceRoot}/vendor/phpunit/phpunit/phpunit", + // "args": ["--filter", "testDefinitionForSelfKeyword"], + "cwd": "${workspaceRoot}" + }, + { + "name": "Listen for XDebug", + "type": "php", + "request": "launch", + "port": 9000 + }, + { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 9000 + } + ] } diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 620527e..c9d1400 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -264,13 +264,38 @@ class DefinitionResolver // Other references are references to a global symbol that have an FQN // Find out the FQN $fqn = $this->resolveReferenceNodeToFqn($node); - if ($fqn === null) { + if (!$fqn) { return null; } + + if ($fqn === 'self' || $fqn === 'static') { + // Resolve self and static keywords to the containing class + // (This is not 100% correct for static but better than nothing) + $classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class); + if (!$classNode) { + return; + } + $fqn = (string)$classNode->getNamespacedName(); + if (!$fqn) { + return; + } + } else if ($fqn === 'parent') { + // Resolve parent keyword to the base class FQN + $classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class); + if (!$classNode || !$classNode->classBaseClause || !$classNode->classBaseClause->baseClass) { + return; + } + $fqn = (string)$classNode->classBaseClause->baseClass->getResolvedName(); + if (!$fqn) { + return; + } + } + // If the node is a function or constant, it could be namespaced, but PHP falls back to global // http://php.net/manual/en/language.namespaces.fallback.php // TODO - verify that this is not a method $globalFallback = ParserHelpers\isConstantFetch($node) || $parent instanceof Node\Expression\CallExpression; + // Return the Definition object from the index index return $this->index->getDefinition($fqn, $globalFallback); } @@ -278,6 +303,7 @@ class DefinitionResolver /** * Given any node, returns the FQN of the symbol that is referenced * Returns null if the FQN could not be resolved or the reference node references a variable + * May also return "static", "self" or "parent" * * @param Node $node * @return string|null diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index 58de8db..1daabe7 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -140,8 +140,9 @@ class TreeAnalyzer $this->definitionNodes[$fqn] = $node; $this->definitions[$fqn] = $this->definitionResolver->createDefinitionFromNode($node, $fqn); } else { + $parent = $node->parent; - if (!( + if ( ( // $node->parent instanceof Node\Expression\ScopedPropertyAccessExpression || ($node instanceof Node\Expression\ScopedPropertyAccessExpression || @@ -150,41 +151,68 @@ class TreeAnalyzer $node->parent instanceof Node\Expression\CallExpression || $node->memberName instanceof PhpParser\Token )) - || ($parent instanceof Node\Statement\NamespaceDefinition && $parent->name !== null && $parent->name->getStart() === $node->getStart())) + || ($parent instanceof Node\Statement\NamespaceDefinition && $parent->name !== null && $parent->name->getStart() === $node->getStart()) ) { - $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node); - if ($fqn !== null) { - $this->addReference($fqn, $node); + return; + } - if ( - $node instanceof Node\QualifiedName - && ($node->isQualifiedName() || $node->parent instanceof Node\NamespaceUseClause) - && !($parent instanceof Node\Statement\NamespaceDefinition && $parent->name->getStart() === $node->getStart() - ) - ) { - // Add references for each referenced namespace - $ns = $fqn; - while (($pos = strrpos($ns, '\\')) !== false) { - $ns = substr($ns, 0, $pos); - $this->addReference($ns, $node); - } - } + $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node); + if (!$fqn) { + return; + } - // Namespaced constant access and function calls also need to register a reference - // to the global version because PHP falls back to global at runtime - // http://php.net/manual/en/language.namespaces.fallback.php - if (ParserHelpers\isConstantFetch($node) || - ($parent instanceof Node\Expression\CallExpression - && !( - $node instanceof Node\Expression\ScopedPropertyAccessExpression || - $node instanceof Node\Expression\MemberAccessExpression - ))) { - $parts = explode('\\', $fqn); - if (count($parts) > 1) { - $globalFqn = end($parts); - $this->addReference($globalFqn, $node); - } - } + if ($fqn === 'self' || $fqn === 'static') { + // Resolve self and static keywords to the containing class + // (This is not 100% correct for static but better than nothing) + $classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class); + if (!$classNode) { + return; + } + $fqn = (string)$classNode->getNamespacedName(); + if (!$fqn) { + return; + } + } else if ($fqn === 'parent') { + // Resolve parent keyword to the base class FQN + $classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class); + if (!$classNode || !$classNode->classBaseClause || !$classNode->classBaseClause->baseClass) { + return; + } + $fqn = (string)$classNode->classBaseClause->baseClass->getResolvedName(); + if (!$fqn) { + return; + } + } + + $this->addReference($fqn, $node); + + if ( + $node instanceof Node\QualifiedName + && ($node->isQualifiedName() || $node->parent instanceof Node\NamespaceUseClause) + && !($parent instanceof Node\Statement\NamespaceDefinition && $parent->name->getStart() === $node->getStart() + ) + ) { + // Add references for each referenced namespace + $ns = $fqn; + while (($pos = strrpos($ns, '\\')) !== false) { + $ns = substr($ns, 0, $pos); + $this->addReference($ns, $node); + } + } + + // Namespaced constant access and function calls also need to register a reference + // to the global version because PHP falls back to global at runtime + // http://php.net/manual/en/language.namespaces.fallback.php + if (ParserHelpers\isConstantFetch($node) || + ($parent instanceof Node\Expression\CallExpression + && !( + $node instanceof Node\Expression\ScopedPropertyAccessExpression || + $node instanceof Node\Expression\MemberAccessExpression + ))) { + $parts = explode('\\', $fqn); + if (count($parts) > 1) { + $globalFqn = end($parts); + $this->addReference($globalFqn, $node); } } } diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index 04e5976..45d949f 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -122,15 +122,16 @@ abstract class ServerTestCase extends TestCase 0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15))) ], 'TestNamespace\\TestClass' => [ - 0 => new Location($symbolsUri , new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {} - 1 => new Location($referencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass(); - 2 => new Location($referencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod(); - 3 => new Location($referencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty; - 4 => new Location($referencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST; - 5 => new Location($referencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param) - 6 => new Location($referencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass - 7 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty; - 8 => new Location($useUri, new Range(new Position( 4, 4), new Position( 4, 27))), // use TestNamespace\TestClass; + 0 => new Location($symbolsUri, new Range(new Position(48, 13), new Position(48, 17))), // echo self::TEST_CLASS_CONST; + 1 => new Location($symbolsUri , new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {} + 2 => new Location($referencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass(); + 3 => new Location($referencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod(); + 4 => new Location($referencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty; + 5 => new Location($referencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST; + 6 => new Location($referencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param) + 7 => new Location($referencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass + 8 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty; + 9 => new Location($useUri, new Range(new Position( 4, 4), new Position( 4, 27))), // use TestNamespace\TestClass; ], 'TestNamespace\\TestChild' => [ 0 => new Location($referencesUri, new Range(new Position(42, 5), new Position(42, 25))), // echo $child->testProperty; @@ -176,14 +177,15 @@ abstract class ServerTestCase extends TestCase 1 => new Location($globalReferencesUri, new Range(new Position(29, 5), new Position(29, 15))) ], 'TestClass' => [ - 0 => new Location($globalSymbolsUri, new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {} - 1 => new Location($globalReferencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass(); - 2 => new Location($globalReferencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod(); - 3 => new Location($globalReferencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty; - 4 => new Location($globalReferencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST; - 5 => new Location($globalReferencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param) - 6 => new Location($globalReferencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass - 7 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty; + 0 => new Location($globalSymbolsUri, new Range(new Position(48, 13), new Position(48, 17))), // echo self::TEST_CLASS_CONST; + 1 => new Location($globalSymbolsUri, new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {} + 2 => new Location($globalReferencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass(); + 3 => new Location($globalReferencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod(); + 4 => new Location($globalReferencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty; + 5 => new Location($globalReferencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST; + 6 => new Location($globalReferencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param) + 7 => new Location($globalReferencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass + 8 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty; ], 'TestChild' => [ 0 => new Location($globalReferencesUri, new Range(new Position(42, 5), new Position(42, 25))), // echo $child->testProperty; diff --git a/tests/Server/TextDocument/Definition/GlobalTest.php b/tests/Server/TextDocument/Definition/GlobalTest.php index 0988e2a..2a80873 100644 --- a/tests/Server/TextDocument/Definition/GlobalTest.php +++ b/tests/Server/TextDocument/Definition/GlobalTest.php @@ -29,11 +29,23 @@ class GlobalTest extends ServerTestCase $this->assertEquals([], $result); } + public function testDefinitionForSelfKeyword() + { + // echo self::TEST_CLASS_CONST; + // Get definition for self + $reference = $this->getReferenceLocations('TestClass')[0]; + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); + $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + } + public function testDefinitionForClassLike() { // $obj = new TestClass(); // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[0]; + $reference = $this->getReferenceLocations('TestClass')[1]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start @@ -45,7 +57,7 @@ class GlobalTest extends ServerTestCase { // TestClass::staticTestMethod(); // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[1]; + $reference = $this->getReferenceLocations('TestClass')[2]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start @@ -57,7 +69,7 @@ class GlobalTest extends ServerTestCase { // echo TestClass::$staticTestProperty; // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[2]; + $reference = $this->getReferenceLocations('TestClass')[3]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start @@ -69,7 +81,7 @@ class GlobalTest extends ServerTestCase { // TestClass::TEST_CLASS_CONST; // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[3]; + $reference = $this->getReferenceLocations('TestClass')[4]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start @@ -213,7 +225,7 @@ class GlobalTest extends ServerTestCase { // function whatever(TestClass $param) { // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[4]; + $reference = $this->getReferenceLocations('TestClass')[5]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start @@ -225,7 +237,7 @@ class GlobalTest extends ServerTestCase { // function whatever(TestClass $param): TestClass { // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[5]; + $reference = $this->getReferenceLocations('TestClass')[6]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start diff --git a/tests/Server/TextDocument/Definition/NamespacedTest.php b/tests/Server/TextDocument/Definition/NamespacedTest.php index ef66624..395881b 100644 --- a/tests/Server/TextDocument/Definition/NamespacedTest.php +++ b/tests/Server/TextDocument/Definition/NamespacedTest.php @@ -34,7 +34,7 @@ class NamespacedTest extends GlobalTest { // use TestNamespace\TestClass; // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[6]; + $reference = $this->getReferenceLocations('TestClass')[7]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start @@ -46,7 +46,7 @@ class NamespacedTest extends GlobalTest { // use TestNamespace\{TestTrait, TestInterface}; // Get definition for TestInterface - $reference = $this->getReferenceLocations('TestClass')[0]; + $reference = $this->getReferenceLocations('TestClass')[1]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start diff --git a/tests/Server/TextDocument/HoverTest.php b/tests/Server/TextDocument/HoverTest.php index 5010360..2ba49e1 100644 --- a/tests/Server/TextDocument/HoverTest.php +++ b/tests/Server/TextDocument/HoverTest.php @@ -15,7 +15,7 @@ class HoverTest extends ServerTestCase { // $obj = new TestClass(); // Get hover for TestClass - $reference = $this->getReferenceLocations('TestClass')[0]; + $reference = $this->getReferenceLocations('TestClass')[1]; $result = $this->textDocument->hover( new TextDocumentIdentifier($reference->uri), $reference->range->start diff --git a/tests/Server/TextDocument/References/GlobalTest.php b/tests/Server/TextDocument/References/GlobalTest.php index fcb6e71..105dfef 100644 --- a/tests/Server/TextDocument/References/GlobalTest.php +++ b/tests/Server/TextDocument/References/GlobalTest.php @@ -151,7 +151,7 @@ class GlobalTest extends ServerTestCase { // $obj = new TestClass(); // Get references for TestClass - $reference = $this->getReferenceLocations('TestClass')[0]; + $reference = $this->getReferenceLocations('TestClass')[1]; $result = $this->textDocument->references( new ReferenceContext, new TextDocumentIdentifier($reference->uri), diff --git a/tests/Validation/cases/WithReturnTypehints.php.expected.json b/tests/Validation/cases/WithReturnTypehints.php.expected.json index d11f271..8c6b092 100644 --- a/tests/Validation/cases/WithReturnTypehints.php.expected.json +++ b/tests/Validation/cases/WithReturnTypehints.php.expected.json @@ -3,7 +3,7 @@ "Fixtures\\Prophecy\\EmptyClass": [ "./WithReturnTypehints.php" ], - "self": [ + "Fixtures\\Prophecy\\WithReturnTypehints": [ "./WithReturnTypehints.php" ], "Fixtures\\Prophecy\\__CLASS__": [ @@ -11,9 +11,6 @@ ], "__CLASS__": [ "./WithReturnTypehints.php" - ], - "parent": [ - "./WithReturnTypehints.php" ] }, "definitions": { diff --git a/tests/Validation/cases/nameToken.php.expected.json b/tests/Validation/cases/nameToken.php.expected.json index c06217b..37cb4ca 100644 --- a/tests/Validation/cases/nameToken.php.expected.json +++ b/tests/Validation/cases/nameToken.php.expected.json @@ -1,5 +1,9 @@ { - "references": [], + "references": { + "A": [ + "./nameToken.php" + ] + }, "definitions": { "A": { "fqn": "A", diff --git a/tests/Validation/cases/parent1.php.expected.json b/tests/Validation/cases/parent1.php.expected.json index 8b29461..56d7e61 100644 --- a/tests/Validation/cases/parent1.php.expected.json +++ b/tests/Validation/cases/parent1.php.expected.json @@ -2,9 +2,6 @@ "references": { "MyNamespace\\B": [ "./parent1.php" - ], - "parent": [ - "./parent1.php" ] }, "definitions": { diff --git a/tests/Validation/cases/parent3.php.expected.json b/tests/Validation/cases/parent3.php.expected.json index 1e51b84..3954754 100644 --- a/tests/Validation/cases/parent3.php.expected.json +++ b/tests/Validation/cases/parent3.php.expected.json @@ -5,9 +5,6 @@ ], "MyNamespace\\B->b()": [ "./parent3.php" - ], - "parent": [ - "./parent3.php" ] }, "definitions": { diff --git a/tests/Validation/cases/self1.php.expected.json b/tests/Validation/cases/self1.php.expected.json index 2ce8e43..fe2bc63 100644 --- a/tests/Validation/cases/self1.php.expected.json +++ b/tests/Validation/cases/self1.php.expected.json @@ -6,7 +6,7 @@ "MyNamespace\\A::b()": [ "./self1.php" ], - "self": [ + "MyNamespace\\A": [ "./self1.php" ] }, diff --git a/tests/Validation/cases/self2.php.expected.json b/tests/Validation/cases/self2.php.expected.json index cc00e3a..60ab062 100644 --- a/tests/Validation/cases/self2.php.expected.json +++ b/tests/Validation/cases/self2.php.expected.json @@ -6,7 +6,7 @@ "MyNamespace\\A::b()": [ "./self2.php" ], - "self": [ + "MyNamespace\\A": [ "./self2.php" ] }, diff --git a/tests/Validation/cases/self3.php.expected.json b/tests/Validation/cases/self3.php.expected.json index 9f25ba1..7d66c2b 100644 --- a/tests/Validation/cases/self3.php.expected.json +++ b/tests/Validation/cases/self3.php.expected.json @@ -6,7 +6,7 @@ "MyNamespace\\A->b()": [ "./self3.php" ], - "self": [ + "MyNamespace\\A": [ "./self3.php" ] }, diff --git a/tests/Validation/cases/self4.php.expected.json b/tests/Validation/cases/self4.php.expected.json index 7f7b25e..64765b7 100644 --- a/tests/Validation/cases/self4.php.expected.json +++ b/tests/Validation/cases/self4.php.expected.json @@ -1,6 +1,6 @@ { "references": { - "self": [ + "MyNamespace\\A": [ "./self4.php" ], "MyNamespace\\A->addTestFile()": [ diff --git a/tests/Validation/cases/static1.php.expected.json b/tests/Validation/cases/static1.php.expected.json index 453edc8..db1a0d2 100644 --- a/tests/Validation/cases/static1.php.expected.json +++ b/tests/Validation/cases/static1.php.expected.json @@ -6,7 +6,7 @@ "MyNamespace\\A::b()": [ "./static1.php" ], - "static": [ + "MyNamespace\\A": [ "./static1.php" ] }, diff --git a/tests/Validation/cases/static2.php.expected.json b/tests/Validation/cases/static2.php.expected.json index 87e8e9a..bb662cb 100644 --- a/tests/Validation/cases/static2.php.expected.json +++ b/tests/Validation/cases/static2.php.expected.json @@ -6,7 +6,7 @@ "MyNamespace\\A::b()": [ "./static2.php" ], - "static": [ + "MyNamespace\\A": [ "./static2.php" ] }, diff --git a/tests/Validation/cases/static3.php.expected.json b/tests/Validation/cases/static3.php.expected.json index 37524ea..34d3851 100644 --- a/tests/Validation/cases/static3.php.expected.json +++ b/tests/Validation/cases/static3.php.expected.json @@ -3,10 +3,10 @@ "MyNamespace\\B": [ "./static3.php" ], - "MyNamespace\\b()": [ + "static->b()": [ "./static3.php" ], - "b()": [ + "MyNamespace\\A": [ "./static3.php" ] }, diff --git a/tests/Validation/cases/static4.php.expected.json b/tests/Validation/cases/static4.php.expected.json index 4bcdd9d..de71d41 100644 --- a/tests/Validation/cases/static4.php.expected.json +++ b/tests/Validation/cases/static4.php.expected.json @@ -2,6 +2,9 @@ "references": { "MyNamespace\\B": [ "./static4.php" + ], + "MyNamespace\\A": [ + "./static4.php" ] }, "definitions": { From 4f672c24d8c127d88d51eefb32973668e0cc54de Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Sun, 19 Nov 2017 02:41:37 +0100 Subject: [PATCH 49/55] feat(diagnostics): report error when $this is used in a static method or outside a class method (#528) --- .../diagnostics/baselines/this_in_method.php | 9 ++ .../diagnostics/errors/this_in_function.php | 6 + fixtures/diagnostics/errors/this_in_root.php | 3 + .../errors/this_in_static_method.php | 9 ++ src/TreeAnalyzer.php | 19 +++ tests/Diagnostics/InvalidThisUsageTest.php | 118 ++++++++++++++++++ 6 files changed, 164 insertions(+) create mode 100644 fixtures/diagnostics/baselines/this_in_method.php create mode 100644 fixtures/diagnostics/errors/this_in_function.php create mode 100644 fixtures/diagnostics/errors/this_in_root.php create mode 100644 fixtures/diagnostics/errors/this_in_static_method.php create mode 100644 tests/Diagnostics/InvalidThisUsageTest.php diff --git a/fixtures/diagnostics/baselines/this_in_method.php b/fixtures/diagnostics/baselines/this_in_method.php new file mode 100644 index 0000000..0463b79 --- /dev/null +++ b/fixtures/diagnostics/baselines/this_in_method.php @@ -0,0 +1,9 @@ +start, $error->length, $this->sourceFileNode->fileContents); @@ -92,6 +93,24 @@ class TreeAnalyzer 'php' ); } + + // Check for invalid usage of $this. + if ($node instanceof Node\Expression\Variable && $node->getName() === 'this') { + // Find the first ancestor that's a class method. Return an error + // if there is none, or if the method is static. + $method = $node->getFirstAncestor(Node\MethodDeclaration::class); + if ($method === null || $method->isStatic()) { + $this->diagnostics[] = new Diagnostic( + $method === null + ? "\$this can only be used in an object context." + : "\$this can not be used in static methods.", + Range::fromNode($node), + null, + DiagnosticSeverity::ERROR, + 'php' + ); + } + } } /** diff --git a/tests/Diagnostics/InvalidThisUsageTest.php b/tests/Diagnostics/InvalidThisUsageTest.php new file mode 100644 index 0000000..7542918 --- /dev/null +++ b/tests/Diagnostics/InvalidThisUsageTest.php @@ -0,0 +1,118 @@ +getDiagnostics(); + } + + /** + * Assertions about a diagnostic. + * + * @param Diagnostic|null $diagnostic + * @param int $message + * @param string $severity + * @param Range $range + */ + private function assertDiagnostic($diagnostic, $message, $severity, $range) + { + $this->assertInstanceOf(Diagnostic::class, $diagnostic); + $this->assertEquals($message, $diagnostic->message); + $this->assertEquals($severity, $diagnostic->severity); + $this->assertEquals($range, $diagnostic->range); + } + + public function testThisInStaticMethodProducesError() + { + $diagnostics = $this->collectDiagnostics( + __DIR__ . '/../../fixtures/diagnostics/errors/this_in_static_method.php' + ); + + $this->assertCount(1, $diagnostics); + $this->assertDiagnostic( + $diagnostics[0], + '$this can not be used in static methods.', + DiagnosticSeverity::ERROR, + new Range( + new Position(6, 15), + new Position(6, 20) + ) + ); + } + + public function testThisInFunctionProducesError() + { + $diagnostics = $this->collectDiagnostics( + __DIR__ . '/../../fixtures/diagnostics/errors/this_in_function.php' + ); + + $this->assertCount(1, $diagnostics); + $this->assertDiagnostic( + $diagnostics[0], + '$this can only be used in an object context.', + DiagnosticSeverity::ERROR, + new Range( + new Position(4, 11), + new Position(4, 16) + ) + ); + } + + public function testThisInRoot() + { + $diagnostics = $this->collectDiagnostics( + __DIR__ . '/../../fixtures/diagnostics/errors/this_in_root.php' + ); + + $this->assertCount(1, $diagnostics); + $this->assertDiagnostic( + $diagnostics[0], + '$this can only be used in an object context.', + DiagnosticSeverity::ERROR, + new Range( + new Position(2, 5), + new Position(2, 10) + ) + ); + } + + public function testThisInMethodProducesNoError() + { + $diagnostics = $this->collectDiagnostics( + __DIR__ . '/../../fixtures/diagnostics/baselines/this_in_method.php' + ); + + $this->assertCount(0, $diagnostics); + } +} From 724eb6f1dced0248e86c0496e301936506d5b73e Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Tue, 21 Nov 2017 03:41:03 -0800 Subject: [PATCH 50/55] ci(appveyor): update image --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index fa6d26a..7d0525c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,6 @@ version: '{build}' +image: Visual Studio 2017 platform: - x64 From 31bae23912a54ced808d9f4df525fcba3f7c929a Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 22 Nov 2017 03:24:24 -0800 Subject: [PATCH 51/55] ci(release): use semantic-release v10 --- .travis.yml | 10 ++-------- dependencies.yml | 12 +++++++++++- package.json | 20 +++++++++++++++++--- release-docker.php | 12 ------------ 4 files changed, 30 insertions(+), 24 deletions(-) delete mode 100755 release-docker.php diff --git a/.travis.yml b/.travis.yml index 9b247b3..81d6911 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,6 @@ git: depth: 10 submodules: false -env: - global: - - BUILD_LEADER_ID=4 - cache: directories: - $HOME/Library/Caches/Homebrew @@ -45,14 +41,12 @@ jobs: services: - docker install: - - git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/* - - git fetch --tags - composer install --prefer-dist --no-interaction - nvm install 8 - nvm use 8 - npm install - script: skip - after_success: + script: + - docker build -t felixfbecker/php-language-server . - npm run semantic-release stages: diff --git a/dependencies.yml b/dependencies.yml index 320ae87..bf6bd77 100644 --- a/dependencies.yml +++ b/dependencies.yml @@ -1,10 +1,20 @@ collectors: +# pull requests for new major versions - type: php-composer path: / actors: - # pull requests for new major versions - type: php-composer versions: "Y.0.0" settings: commit_message_prefix: "chore: " +- type: js-npm + path: / + settings: + dist_tags: + semantic-release: next + actors: + - type: js-npm + versions: "Y.0.0" + settings: + commit_message_prefix: "chore: " diff --git a/package.json b/package.json index 75f02e1..6d6fe31 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,16 @@ "private": true, "scripts": { "commitmsg": "validate-commit-msg", - "semantic-release": "semantic-release pre && php release-docker.php && semantic-release post" + "semantic-release": "semantic-release" }, "devDependencies": { + "@semantic-release/github": "^1.0.0", + "@semantic-release/last-release-git-tag": "^1.0.0", "cz-conventional-changelog": "^2.0.0", "husky": "^0.14.3", "last-release-git": "0.0.3", - "semantic-release": "^8.2.0", + "semantic-release": "^10.0.0", + "semantic-release-docker": "^1.0.0", "validate-commit-msg": "^2.14.0" }, "config": { @@ -19,7 +22,18 @@ } }, "release": { - "getLastRelease": "last-release-git" + "verifyConditions": [ + "@semantic-release/github", + "semantic-release-docker" + ], + "getLastRelease": "@semantic-release/last-release-git-tag", + "publish": [ + "@semantic-release/github", + { + "path": "semantic-release-docker", + "name": "felixfbecker/php-language-server" + } + ] }, "repository": { "type": "git", diff --git a/release-docker.php b/release-docker.php deleted file mode 100755 index f833d60..0000000 --- a/release-docker.php +++ /dev/null @@ -1,12 +0,0 @@ -version; - -system("docker login -e=$dockerEmail -u=$dockerUsername -p=$dockerPassword"); -system("docker build -t felixfbecker/php-language-server:latest ."); -system("docker tag felixfbecker/php-language-server:latest felixfbecker/php-language-server:$version ."); -system("docker push felixfbecker/php-language-server:$version"); -system("docker push felixfbecker/php-language-server:latest"); From ff746a836d5d7d9a05e39afa32c9a4e8600ce7c3 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 25 Nov 2017 10:52:21 -0800 Subject: [PATCH 52/55] chore: update semantic-release to v11 --- package.json | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 6d6fe31..828931f 100644 --- a/package.json +++ b/package.json @@ -7,13 +7,12 @@ "semantic-release": "semantic-release" }, "devDependencies": { - "@semantic-release/github": "^1.0.0", - "@semantic-release/last-release-git-tag": "^1.0.0", + "@semantic-release/github": "^2.0.0", + "@semantic-release/last-release-git-tag": "^2.0.0", "cz-conventional-changelog": "^2.0.0", "husky": "^0.14.3", - "last-release-git": "0.0.3", - "semantic-release": "^10.0.0", - "semantic-release-docker": "^1.0.0", + "semantic-release": "^11.0.0", + "semantic-release-docker": "^2.0.0", "validate-commit-msg": "^2.14.0" }, "config": { From 9b1fafae58b6c596d2627002f032ef5e87605ace Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Sun, 3 Dec 2017 22:42:01 +0100 Subject: [PATCH 53/55] fix(diagnostics): update checking of $this usage to only error in static methods (#545) --- .../diagnostics/errors/this_in_function.php | 6 ---- fixtures/diagnostics/errors/this_in_root.php | 3 -- src/TreeAnalyzer.php | 6 ++-- tests/Diagnostics/InvalidThisUsageTest.php | 36 ------------------- 4 files changed, 2 insertions(+), 49 deletions(-) delete mode 100644 fixtures/diagnostics/errors/this_in_function.php delete mode 100644 fixtures/diagnostics/errors/this_in_root.php diff --git a/fixtures/diagnostics/errors/this_in_function.php b/fixtures/diagnostics/errors/this_in_function.php deleted file mode 100644 index db6139d..0000000 --- a/fixtures/diagnostics/errors/this_in_function.php +++ /dev/null @@ -1,6 +0,0 @@ -getFirstAncestor(Node\MethodDeclaration::class); - if ($method === null || $method->isStatic()) { + if ($method->isStatic()) { $this->diagnostics[] = new Diagnostic( - $method === null - ? "\$this can only be used in an object context." - : "\$this can not be used in static methods.", + "\$this can not be used in static methods.", Range::fromNode($node), null, DiagnosticSeverity::ERROR, diff --git a/tests/Diagnostics/InvalidThisUsageTest.php b/tests/Diagnostics/InvalidThisUsageTest.php index 7542918..b7e8196 100644 --- a/tests/Diagnostics/InvalidThisUsageTest.php +++ b/tests/Diagnostics/InvalidThisUsageTest.php @@ -71,42 +71,6 @@ class InvalidThisUsageTest extends TestCase ); } - public function testThisInFunctionProducesError() - { - $diagnostics = $this->collectDiagnostics( - __DIR__ . '/../../fixtures/diagnostics/errors/this_in_function.php' - ); - - $this->assertCount(1, $diagnostics); - $this->assertDiagnostic( - $diagnostics[0], - '$this can only be used in an object context.', - DiagnosticSeverity::ERROR, - new Range( - new Position(4, 11), - new Position(4, 16) - ) - ); - } - - public function testThisInRoot() - { - $diagnostics = $this->collectDiagnostics( - __DIR__ . '/../../fixtures/diagnostics/errors/this_in_root.php' - ); - - $this->assertCount(1, $diagnostics); - $this->assertDiagnostic( - $diagnostics[0], - '$this can only be used in an object context.', - DiagnosticSeverity::ERROR, - new Range( - new Position(2, 5), - new Position(2, 10) - ) - ); - } - public function testThisInMethodProducesNoError() { $diagnostics = $this->collectDiagnostics( From 09477b747efc254aedfd4485a79f084a7fc4904a Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 3 Dec 2017 15:49:43 -0800 Subject: [PATCH 54/55] fix(diagnostics): handle null case --- src/TreeAnalyzer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index c6bd6d7..465f5bb 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -99,7 +99,7 @@ class TreeAnalyzer // Find the first ancestor that's a class method. Return an error // if there is none, or if the method is static. $method = $node->getFirstAncestor(Node\MethodDeclaration::class); - if ($method->isStatic()) { + if ($method && $method->isStatic()) { $this->diagnostics[] = new Diagnostic( "\$this can not be used in static methods.", Range::fromNode($node), From 78316545a8158b24052721ca451f735552291ff4 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 3 Dec 2017 16:23:14 -0800 Subject: [PATCH 55/55] ci(macos): try alternative method to download composer --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 81d6911..1301d4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,8 @@ jobs: - brew tap homebrew/homebrew-php - brew install php71 - brew install homebrew/php/php71-xdebug - - curl https://getcomposer.org/installer | php + - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" + - php composer-setup.php - ln -s "`pwd`/composer.phar" /usr/local/bin/composer - stage: release php: '7.0'