result = new PhpStanConfigurableForm<>(myFixture.getProject(), new PhpStanConfiguration())
21 | .validateMessage(message);
22 | assertTrue(result.first);
23 | assertEquals("OK, " + message, result.second);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/testData/codeInsight/typeInference/Templates.php:
--------------------------------------------------------------------------------
1 | $c = (new A())->mirror($a, $b);
21 |
22 | namespace BB;
23 |
24 | /**
25 | * @phpstan-template T
26 | * @phpstan-template T1
27 | */
28 | abstract class Base
29 | {
30 |
31 |
32 | /**
33 | * @phpstan-var T
34 | */
35 | public $first;
36 |
37 | /**
38 | * @phpstan-var T1
39 | */
40 | public $second;
41 | }
42 |
43 | class P
44 | {
45 | }
46 | class P1
47 | {
48 | }
49 |
50 |
51 | /**
52 | * @phpstan-extends Base
53 | */
54 | class Child extends Base
55 | {
56 | }
57 |
58 | /**
59 | * @phpstan-extends Base
60 | */
61 | class ChildPartial extends Base
62 | {
63 | }
64 |
65 | (new Child())->first;
66 | (new Child())->second;
67 | (new ChildPartial())->first;
68 | (new ChildPartial())->second;
--------------------------------------------------------------------------------
/tests/com/jetbrains/php/phpstan/lang/documentation/parser/PhpStanDocParserTest.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.phpstan.lang.documentation.parser;
2 |
3 | import com.jetbrains.php.lang.parser.BasicPhpParserTestCase;
4 |
5 | public class PhpStanDocParserTest extends BasicPhpParserTestCase {
6 | public static final String TEST_DATA_HOME = "/phpstorm/phpstan/testData/";
7 |
8 | @Override
9 | protected String getDataPath() {
10 | return TEST_DATA_HOME + "parser";
11 | }
12 |
13 | public void test$DocTags() throws Throwable {
14 | doTest();
15 | }
16 |
17 | public void test$DocMethodsTags() throws Throwable {
18 | doTest();
19 | }
20 |
21 | public void test$Template() throws Throwable {
22 | doTest();
23 | }
24 |
25 | public void test$Assert() throws Throwable {
26 | doTest();
27 | }
28 |
29 | public void test$Inheritance() throws Throwable {
30 | doTest();
31 | }
32 |
33 | public void test$Method() throws Throwable {
34 | doTest();
35 | }
36 |
37 | public void test$Type() throws Throwable {
38 | doTest();
39 | }
40 |
41 | public void test$InplaceCovariantGeneric() throws Throwable {
42 | doTest();
43 | }
44 |
45 | public void test$InplaceContravariantGeneric() throws Throwable {
46 | doTest();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanConfigurable.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.intellij.openapi.options.Configurable;
4 | import com.intellij.openapi.project.Project;
5 | import com.jetbrains.php.tools.quality.QualityToolConfigurationComboBox;
6 | import com.jetbrains.php.tools.quality.QualityToolProjectConfigurableForm;
7 | import com.jetbrains.php.tools.quality.QualityToolType;
8 | import com.jetbrains.php.tools.quality.QualityToolsOptionsPanel;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | public class PhpStanConfigurable extends QualityToolProjectConfigurableForm implements Configurable.NoScroll {
12 |
13 | public PhpStanConfigurable(@NotNull Project project) {
14 | super(project);
15 | }
16 |
17 | @Override
18 | protected QualityToolsOptionsPanel getQualityToolOptionPanel(QualityToolConfigurationComboBox configurationBox, Runnable validate) {
19 | return new PhpStanOptionsPanel(myProject, configurationBox, validate);
20 | }
21 |
22 | @Override
23 | public @NotNull String getId() {
24 | return "settings.php.quality.tools.phpstan";
25 | }
26 |
27 | @Override
28 | protected @NotNull QualityToolType getQualityToolType() {
29 | return PhpStanQualityToolType.INSTANCE;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanSettingsTransferStartupActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan
2 |
3 | import com.intellij.openapi.application.ApplicationManager
4 | import com.intellij.openapi.project.Project
5 | import com.intellij.openapi.startup.ProjectActivity
6 | import com.intellij.profile.codeInspection.InspectionProfileManager
7 | import com.intellij.util.PlatformUtils
8 |
9 | private class PhpStanSettingsTransferStartupActivity : ProjectActivity {
10 | override suspend fun execute(project: Project) {
11 | if (project.isDefault) return
12 | val app = ApplicationManager.getApplication()
13 | if (app.isUnitTestMode || app.isHeadlessEnvironment || !PlatformUtils.isPhpStorm()) return
14 |
15 | val tool = PhpStanQualityToolType.INSTANCE.getGlobalTool(project, InspectionProfileManager.getInstance(project).currentProfile) as? PhpStanGlobalInspection
16 | val instance = PhpStanOptionsConfiguration.getInstance(project)
17 | tool?.let {
18 | if (!instance.isTransferred) {
19 | instance.config = tool.config
20 | instance.autoload = tool.autoload
21 | instance.level = tool.level
22 | instance.memoryLimit = tool.memoryLimit
23 | instance.isFullProject = tool.FULL_PROJECT
24 | instance.isTransferred = true
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanConfigurationBaseManager.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.intellij.openapi.util.NlsSafe;
4 | import com.intellij.util.xmlb.XmlSerializer;
5 | import com.jetbrains.php.tools.quality.QualityToolConfigurationBaseManager;
6 | import com.jetbrains.php.tools.quality.QualityToolType;
7 | import org.jdom.Element;
8 | import org.jetbrains.annotations.NotNull;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | public class PhpStanConfigurationBaseManager extends QualityToolConfigurationBaseManager {
12 | private static final @NlsSafe String PHP_STAN_PATH = "PhpStanPath";
13 | public static final @NlsSafe String PHP_STAN = "PHPStan";
14 | private static final @NlsSafe String ROOT_NAME = "PhpStan_settings";
15 |
16 | @Override
17 | protected @NotNull QualityToolType getQualityToolType() {
18 | return PhpStanQualityToolType.INSTANCE;
19 | }
20 |
21 | @Override
22 | protected @NotNull String getOldStyleToolPathName() {
23 | return PHP_STAN_PATH;
24 | }
25 |
26 | @Override
27 | protected @NotNull String getConfigurationRootName() {
28 | return ROOT_NAME;
29 | }
30 |
31 | @Override
32 | protected @Nullable PhpStanConfiguration loadLocal(Element element) {
33 | return XmlSerializer.deserialize(element, PhpStanConfiguration.class);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/testData/codeInsight/documentationProvider/PhpStanTags.html:
--------------------------------------------------------------------------------
1 |
2 | |
3 | Templates: | T of Foo – with description
4 | covariant M of Foo – with description |
5 |
|
6 | Parameters: | int $a – cshdcygdc
7 | array<int, Foo> $b |
8 |
|
9 | Returns: | Foo |
10 |
|
11 | Throws: | Exception |
12 |
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanConfigurationManager.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.intellij.openapi.application.ApplicationManager;
4 | import com.intellij.openapi.components.State;
5 | import com.intellij.openapi.components.Storage;
6 | import com.intellij.openapi.project.Project;
7 | import com.jetbrains.php.tools.quality.QualityToolConfigurationManager;
8 | import org.jetbrains.annotations.NotNull;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | public class PhpStanConfigurationManager extends QualityToolConfigurationManager {
12 |
13 | public static final int DEFAULT_MAX_MESSAGES_PER_FILE = 50;
14 |
15 | public PhpStanConfigurationManager(@Nullable Project project) {
16 | super(project);
17 | if (project != null) {
18 | myProjectManager = project.getService(PhpStanProjectConfigurationManager.class);
19 | }
20 | myApplicationManager = ApplicationManager.getApplication().getService(PhpStanAppConfigurationManager.class);
21 | }
22 |
23 | public static PhpStanConfigurationManager getInstance(@NotNull Project project) {
24 | return project.getService(PhpStanConfigurationManager.class);
25 | }
26 |
27 | @State(name = "PhpStan", storages = @Storage("php.xml"))
28 | static class PhpStanProjectConfigurationManager extends PhpStanConfigurationBaseManager {
29 | }
30 |
31 | @State(name = "PhpStan", storages = @Storage("php.xml"))
32 | static class PhpStanAppConfigurationManager extends PhpStanConfigurationBaseManager {
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanProjectConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.intellij.openapi.components.PersistentStateComponent;
4 | import com.intellij.openapi.components.State;
5 | import com.intellij.openapi.components.Storage;
6 | import com.intellij.openapi.components.StoragePathMacros;
7 | import com.intellij.openapi.project.Project;
8 | import com.intellij.util.xmlb.XmlSerializerUtil;
9 | import com.jetbrains.php.tools.quality.QualityToolProjectConfiguration;
10 | import com.jetbrains.php.tools.quality.QualityToolType;
11 | import org.jetbrains.annotations.NotNull;
12 | import org.jetbrains.annotations.Nullable;
13 |
14 | @State(name = "PhpStanProjectConfiguration", storages = @Storage(StoragePathMacros.WORKSPACE_FILE))
15 | public class PhpStanProjectConfiguration extends QualityToolProjectConfiguration
16 | implements PersistentStateComponent {
17 |
18 | @Override
19 | public @Nullable PhpStanProjectConfiguration getState() {
20 | return this;
21 | }
22 |
23 | @Override
24 | public void loadState(@NotNull PhpStanProjectConfiguration state) {
25 | XmlSerializerUtil.copyBean(state, this);
26 | }
27 |
28 | @Override
29 | protected QualityToolType getQualityToolType() {
30 | return PhpStanQualityToolType.INSTANCE;
31 | }
32 |
33 | public static PhpStanProjectConfiguration getInstance(Project project) {
34 | return project.getService(PhpStanProjectConfiguration.class);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/testData/parser/Inheritance.txt:
--------------------------------------------------------------------------------
1 |
28 | PsiWhiteSpace('\n ')
29 | PsiElement(DOC_LEADING_ASTERISK)('*')
30 | PsiWhiteSpace(' ')
31 | PhpDocTagImpl: @phpstan-require-implemen...
32 | PsiElement(DOC_TAG_NAME)('@phpstan-require-implements')
33 | PsiWhiteSpace(' ')
34 | PhpDocTypeImpl: A
35 | PsiElement(DOC_IDENTIFIER)('A')
36 | PhpPsiElementImpl
37 |
38 | PsiWhiteSpace('\n ')
39 | PsiElement(DOC_COMMENT_END)('*/')
40 | PsiWhiteSpace('\n')
41 | PhpClass: C
42 | PsiElement(trait)('trait')
43 | PsiWhiteSpace(' ')
44 | PsiElement(identifier)('C')
45 | PsiWhiteSpace(' ')
46 | Extends list
47 |
48 | Implements list
49 |
50 | PsiElement({)('{')
51 | PsiWhiteSpace('\n\n')
52 | PsiElement(})('}')
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanConfigurableForm.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.util.Pair;
5 | import com.intellij.openapi.util.Version;
6 | import com.jetbrains.php.PhpBundle;
7 | import com.jetbrains.php.tools.quality.QualityToolConfigurableForm;
8 | import com.jetbrains.php.tools.quality.QualityToolType;
9 | import org.jetbrains.annotations.NonNls;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | import static com.jetbrains.php.tools.quality.phpstan.PhpStanConfigurationBaseManager.PHP_STAN;
13 |
14 | public class PhpStanConfigurableForm extends QualityToolConfigurableForm {
15 |
16 | public PhpStanConfigurableForm(@NotNull Project project, @NotNull C configuration) {
17 | super(project, configuration, PHP_STAN, "phpstan");
18 | }
19 |
20 | @Override
21 | public QualityToolType getQualityToolType() {
22 | return PhpStanQualityToolType.INSTANCE;
23 | }
24 |
25 | @Override
26 | public String getHelpTopic() {
27 | return "reference.settings.php.PhpStan";
28 | }
29 |
30 | @Override
31 | public @NotNull Pair validateMessage(@NonNls String message) {
32 | final Version version = extractVersion(message.trim().replaceFirst("PHPStan.* ([\\d.]*).*", "$1").trim());
33 | if (version == null || !message.contains(PHP_STAN)) {
34 | return Pair.create(false, PhpBundle.message("quality.tool.can.not.determine.version", message));
35 | }
36 | return Pair.create(true, "OK, " + message);
37 | }
38 | }
--------------------------------------------------------------------------------
/tests/com/jetbrains/php/phpstan/quality/tools/PhpStanAnnotatorTest.kt:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.phpstan.quality.tools
2 |
3 | import com.intellij.profile.codeInspection.InspectionProfileManager
4 | import com.intellij.psi.PsiFile
5 | import com.intellij.testFramework.builders.ModuleFixtureBuilder
6 | import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase
7 | import com.jetbrains.php.tools.quality.phpstan.*
8 |
9 | class PhpStanAnnotatorTest : CodeInsightFixtureTestCase>() {
10 | override fun setUp() {
11 | super.setUp()
12 | PhpStanConfigurationManager.getInstance(myFixture.project).getOrCreateLocalSettings().toolPath = "phpstan"
13 | myFixture.addFileToProject("file.txt", "")
14 | myFixture.configureByText("file.php", "
5 | */
6 | function name($p) {}
7 | ---
8 | PHP file
9 | PsiElement(Non Lazy Group statement)
10 | PsiElement(php opening tag)('')
32 | PhpPsiElementImpl
33 |
34 | PsiWhiteSpace('\n ')
35 | PsiElement(DOC_COMMENT_END)('*/')
36 | PsiWhiteSpace('\n')
37 | FunctionImpl: name
38 | PsiElement(function)('function')
39 | PsiWhiteSpace(' ')
40 | PsiElement(identifier)('name')
41 | PsiElement(()('(')
42 | Parameter list
43 | ParameterImpl: p
44 | PsiElement(variable)('$p')
45 | PsiElement())(')')
46 | PsiWhiteSpace(' ')
47 | PsiElement(Group statement)
48 | PsiElement({)('{')
49 | PsiElement(})('}')
--------------------------------------------------------------------------------
/testData/parser/InplaceContravariantGeneric.txt:
--------------------------------------------------------------------------------
1 |
5 | */
6 | function name($p) {}
7 | ---
8 | PHP file
9 | PsiElement(Non Lazy Group statement)
10 | PsiElement(php opening tag)('')
32 | PhpPsiElementImpl
33 |
34 | PsiWhiteSpace('\n ')
35 | PsiElement(DOC_COMMENT_END)('*/')
36 | PsiWhiteSpace('\n')
37 | FunctionImpl: name
38 | PsiElement(function)('function')
39 | PsiWhiteSpace(' ')
40 | PsiElement(identifier)('name')
41 | PsiElement(()('(')
42 | Parameter list
43 | ParameterImpl: p
44 | PsiElement(variable)('$p')
45 | PsiElement())(')')
46 | PsiWhiteSpace(' ')
47 | PsiElement(Group statement)
48 | PsiElement({)('{')
49 | PsiElement(})('}')
--------------------------------------------------------------------------------
/testData/output/Simple.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/tests/com/jetbrains/php/phpstan/lang/documentation/PhpStanDocumentationProviderTest.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.phpstan.lang.documentation;
2 |
3 | import com.intellij.psi.PsiDocCommentBase;
4 | import com.intellij.util.CollectConsumer;
5 | import com.jetbrains.php.fixtures.PhpCodeInsightFixtureTestCase;
6 | import com.jetbrains.php.lang.documentation.PhpDocumentationProvider;
7 | import com.jetbrains.php.phpstan.lang.documentation.parser.PhpStanDocParserTest;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | public class PhpStanDocumentationProviderTest extends PhpCodeInsightFixtureTestCase {
11 | @Override
12 | protected @NotNull String getTestDataHome() {
13 | return PhpStanDocParserTest.TEST_DATA_HOME;
14 | }
15 |
16 | @Override
17 | protected String getFixtureTestDataFolder() {
18 | return "codeInsight/documentationProvider";
19 | }
20 |
21 | @NotNull
22 | @Override
23 | protected String getFileAfterExtension() {
24 | return "html";
25 | }
26 |
27 | public void doTestRenderedDoc() {
28 | configureByFile();
29 | PhpDocumentationProvider docProvider = new PhpDocumentationProvider();
30 | CollectConsumer docComments = new CollectConsumer<>();
31 | PhpDocumentationProvider provider = new PhpDocumentationProvider();
32 | provider.collectDocComments(myFixture.getFile(), docComments);
33 | StringBuilder builder = new StringBuilder();
34 | docComments.getResult().forEach(c -> {
35 | builder.append(docProvider.generateRenderedDoc(c));
36 | builder.append("\n");
37 | });
38 | String res = builder.toString().replace("
", "
\n").replace("", "\n
").replace("
", "\n").replace("", "\n
");
39 | configurePhpByText("result.html", res);
40 | checkResultByFile();
41 | }
42 |
43 | public void testPhpStanTags() {
44 | doTestRenderedDoc();
45 | }
46 |
47 | public void testVarTag() {
48 | doTestRenderedDoc();
49 | }
50 |
51 | public void testCustomPhpStanTags() {
52 | doTestRenderedDoc();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/intellij.phpstan.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanOptionsConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.intellij.openapi.components.PersistentStateComponent;
4 | import com.intellij.openapi.components.State;
5 | import com.intellij.openapi.components.Storage;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.util.NlsSafe;
8 | import com.intellij.util.xmlb.XmlSerializerUtil;
9 | import com.jetbrains.php.tools.quality.QualityToolsOptionsConfiguration;
10 | import org.jetbrains.annotations.NonNls;
11 | import org.jetbrains.annotations.NotNull;
12 | import org.jetbrains.annotations.Nullable;
13 |
14 | @State(name = "PhpStanOptionsConfiguration", storages = @Storage("php.xml"))
15 | public class PhpStanOptionsConfiguration extends QualityToolsOptionsConfiguration implements PersistentStateComponent {
16 | private boolean fullProject = false;
17 | private @NonNls String memoryLimit = "2G";
18 | private int level = 4;
19 | private @NlsSafe String config = "";
20 | private @NlsSafe String autoload = "";
21 |
22 | public boolean isFullProject() {
23 | return fullProject;
24 | }
25 |
26 | public void setFullProject(boolean fullProject) {
27 | this.fullProject = fullProject;
28 | }
29 |
30 | public String getMemoryLimit() {
31 | return memoryLimit;
32 | }
33 |
34 | public void setMemoryLimit(String memoryLimit) {
35 | this.memoryLimit = memoryLimit;
36 | }
37 |
38 | public int getLevel() {
39 | return level;
40 | }
41 |
42 | public void setLevel(int level) {
43 | this.level = level;
44 | }
45 |
46 | public String getConfig() {
47 | return config;
48 | }
49 |
50 | public void setConfig(String config) {
51 | this.config = config;
52 | }
53 |
54 | public String getAutoload() {
55 | return autoload;
56 | }
57 |
58 | public void setAutoload(String autoload) {
59 | this.autoload = autoload;
60 | }
61 |
62 | @Override
63 | public @Nullable PhpStanOptionsConfiguration getState() {
64 | return this;
65 | }
66 |
67 | @Override
68 | public void loadState(@NotNull PhpStanOptionsConfiguration state) {
69 | XmlSerializerUtil.copyBean(state, this);
70 | }
71 |
72 | public static PhpStanOptionsConfiguration getInstance(Project project) {
73 | return project.getService(PhpStanOptionsConfiguration.class);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/testData/output/Simple.php:
--------------------------------------------------------------------------------
1 | protected $type;
9 | protected $data;
10 |
11 | public function __construct($type, $data)
12 | {
13 | switch ($type) {
14 | case self::TYPE_XML:
15 | break;
16 | default:
17 | throw new \WebServCo\Framework\Exceptions\ApplicationException('Type not implemented.');
18 | break;
19 | }
20 | $this->type = $type;
21 | $this->data = $data;
22 | }
23 |
24 | public function highlight()
25 | {
26 | switch ($this->type) {
27 | case self::TYPE_XML:
28 | return $this->highlightXml($this->data);
29 | break;
30 | default:
31 | return false;
32 | break;
33 | }
34 | }
35 |
36 | protected function highlightXml($data)
37 | {
38 | $data = htmlentities($data);
39 | $data = str_replace('<', '<', $data);
40 | $data = str_replace('>', '>', $data);
41 | return $data;
42 | }
43 | }
44 | \count();
45 |
--------------------------------------------------------------------------------
/testData/codeInsight/documentationProvider/CustomPhpStanTags.html:
--------------------------------------------------------------------------------
1 |
2 | |
3 | Templates: | NewT |
4 |
|
5 | Phpstan-assert: | string[] $arr |
6 |
|
7 | Psalm-check-type: | $bar = int |
8 |
|
9 | Psalm-type: | PhoneType = array{phone: string} |
10 |
|
11 | Phpstan-param-out: | int $a |
12 |
|
13 | Phpstan-if-this-is: | a<int> |
14 |
|
15 | Phpstan-assert-if-false: | int $this->test() |
16 |
|
17 | Phpstan-assert-if-true: | null $this->b |
18 |
|
19 | Phpstan-this-out: | self<NewT> |
20 |
|
21 | Phpstan-self-out: | self<NewT> |
22 |
--------------------------------------------------------------------------------
/tests/com/jetbrains/php/phpstan/quality/tools/PhpStanConfigFileFromComposerTest.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.phpstan.quality.tools;
2 |
3 | import com.intellij.openapi.vfs.VirtualFile;
4 | import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase;
5 | import com.jetbrains.php.tools.quality.phpstan.PhpStanComposerConfig;
6 | import org.jetbrains.annotations.NotNull;
7 | import org.jetbrains.annotations.Nullable;
8 |
9 | public class PhpStanConfigFileFromComposerTest extends CodeInsightFixtureTestCase {
10 | private void doRulesetTest(@NotNull String content, @Nullable String expectedPath) {
11 | myFixture.configureByText("composer.json", content);
12 | VirtualFile configFile = myFixture.getFile().getVirtualFile();
13 | assertEquals(expectedPath, new PhpStanComposerConfig().getRuleset(configFile));
14 | }
15 |
16 | public void testCFormat() {
17 | doRulesetTest("""
18 |
19 | {
20 | "scripts": {
21 | "phpstan": "vendor/bin/phpstan analyse -l 2 -c tests/phpstan/phpstan.neon system/src --memory-limit=256M"
22 | }
23 | }
24 | """, "tests/phpstan/phpstan.neon");
25 | }
26 |
27 | public void testNoScriptsSection() {
28 | doRulesetTest("""
29 |
30 | {
31 | }
32 | """, null);
33 | }
34 |
35 | public void testNoPHPStanInScriptsSection() {
36 | doRulesetTest("""
37 |
38 | {
39 | "scripts": {
40 | }
41 | }
42 | """, null);
43 | }
44 |
45 | public void testConfigFormat() {
46 | doRulesetTest("""
47 |
48 | {
49 | "scripts": {
50 | "phpstan": "vendor/bin/phpstan analyse -l 2 --configuration=tests/phpstan/phpstan.neon system/src --memory-limit=256M"
51 | }
52 | }
53 | """, "tests/phpstan/phpstan.neon");
54 | }
55 |
56 | public void testNoConfigArg() {
57 | doRulesetTest("""
58 |
59 | {
60 | "scripts": {
61 | "phpstan": "vendor/bin/phpstan analyse -l 2 system/src --memory-limit=256M"
62 | }
63 | }
64 | """, null);
65 | }
66 |
67 | public void testWrongFormat() {
68 | doRulesetTest("""
69 |
70 | {
71 | "scripts": {
72 | "phpstan": "vendor/bin/phpstan analyse -l 2 -c"
73 | }
74 | }
75 | """, null);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/testData/parser/Method.txt:
--------------------------------------------------------------------------------
1 |
33 | PsiWhiteSpace('\n ')
34 | PsiElement(DOC_LEADING_ASTERISK)('*')
35 | PsiWhiteSpace(' ')
36 | PhpDocPropertyTagImpl: @phpstan-property-read
37 | PsiElement(DOC_TAG_NAME)('@phpstan-property-read')
38 | PsiWhiteSpace(' ')
39 | PhpDocTypeImpl: int
40 | PsiElement(DOC_IDENTIFIER)('int')
41 | PsiWhiteSpace(' ')
42 | PhpDocPropertyImpl: b
43 | PsiElement(DOC_VARIABLE)('$b')
44 | PhpPsiElementImpl
45 |
46 | PsiWhiteSpace('\n ')
47 | PsiElement(DOC_LEADING_ASTERISK)('*')
48 | PsiWhiteSpace(' ')
49 | PhpDocPropertyTagImpl: @phpstan-property-write
50 | PsiElement(DOC_TAG_NAME)('@phpstan-property-write')
51 | PsiWhiteSpace(' ')
52 | PhpDocTypeImpl: int
53 | PsiElement(DOC_IDENTIFIER)('int')
54 | PsiWhiteSpace(' ')
55 | PhpDocPropertyImpl: c
56 | PsiElement(DOC_VARIABLE)('$c')
57 | PhpPsiElementImpl
58 |
59 | PsiWhiteSpace('\n ')
60 | PsiElement(DOC_LEADING_ASTERISK)('*')
61 | PsiWhiteSpace(' ')
62 | PhpDocMethodTagImpl: phpstan-method
63 | PsiElement(error silence)('@')
64 | PsiElement(identifier)('phpstan-method')
65 | PsiWhiteSpace(' ')
66 | PhpDocMethodImpl: f
67 | PsiElement(identifier)('f')
68 | PsiElement(()('(')
69 | Parameter list
70 |
71 | PsiElement())(')')
72 | PhpPsiElementImpl
73 |
74 | PsiWhiteSpace('\n ')
75 | PsiElement(DOC_COMMENT_END)('*/')
76 | PsiWhiteSpace('\n')
77 | PhpClass: F
78 | PsiElement(class)('class')
79 | PsiWhiteSpace(' ')
80 | PsiElement(identifier)('F')
81 | PsiWhiteSpace(' ')
82 | Extends list
83 |
84 | Implements list
85 |
86 | PsiElement({)('{')
87 | PsiWhiteSpace('\n\n')
88 | PsiElement(})('}')
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ### Code of Conduct
2 |
3 | This code of conduct outlines our expectations for all those who participate in our open source projects and communities (community programs), as well as the consequences for unacceptable behaviour. We invite all those who participate to help us create safe and positive experiences for everyone. Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.
4 |
5 | #### How to behave
6 | The following behaviours are expected and requested of all community members:
7 |
8 | * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
9 | * Exercise consideration, respect and empathy in your speech and actions. Remember, we have all been through different stages of learning when adopting technologies.
10 | * Refrain from demeaning, discriminatory, or harassing behaviour and speech.
11 | * Disagreements on things are fine, argumentative behaviour or trolling are not.
12 |
13 | #### How not to behave
14 |
15 | * Do not perform threats of violence or use violent language directed against another person.
16 | * Do not make jokes of sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory nature, or use language of this nature.
17 | * Do not post or display sexually explicit or violent material.
18 | * Do not post or threaten to post other people’s personally identifying information ("doxing").
19 | * Do not make personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
20 | * Do not engage in sexual attention. This includes, sexualised comments or jokes and sexual advances.
21 | * Do not advocate for, or encourage, any of the above behaviour.
22 |
23 | Please take into account that online communities bring together people from many different cultures and backgrounds. It's important to understand that sometimes the combination of cultural differences and online interaction can lead to misunderstandings. That is why having empathy is very important.
24 |
25 | #### How to report issues
26 |
27 | If someone is acting inappropriately or violating this Code of Conduct in any shape or form, and they are not receptive to your feedback or you prefer not to confront them, please reach out to JetBrains via codeofconduct@jetbrains.com
28 |
29 | #### Consequences of Unacceptable Behaviour
30 |
31 | Unacceptable behaviour from any community member will not be tolerated. Anyone asked to stop unacceptable behaviour is expected to comply immediately. If a community member engages in unacceptable behaviour, JetBrains and/or community organisers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning.
32 |
33 | ##### License and attribution
34 | The license is based off of The Citizen Code of Conduct is distributed by Stumptown Syndicate under a Creative Commons Attribution-ShareAlike license.
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/remote/PhpStanRemoteConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan.remote;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.util.NlsContexts;
5 | import com.intellij.openapi.util.NlsSafe;
6 | import com.intellij.util.xmlb.annotations.Attribute;
7 | import com.intellij.util.xmlb.annotations.Tag;
8 | import com.jetbrains.php.PhpBundle;
9 | import com.jetbrains.php.config.interpreters.PhpInterpretersManagerImpl;
10 | import com.jetbrains.php.config.interpreters.PhpSdkDependentConfiguration;
11 | import com.jetbrains.php.tools.quality.phpstan.PhpStanBundle;
12 | import com.jetbrains.php.tools.quality.phpstan.PhpStanConfiguration;
13 | import org.jetbrains.annotations.Nls;
14 | import org.jetbrains.annotations.NotNull;
15 | import org.jetbrains.annotations.Nullable;
16 |
17 | import static com.intellij.openapi.util.text.StringUtil.isEmpty;
18 | import static com.jetbrains.php.tools.quality.QualityToolProjectConfiguration.DEFAULT_INTERPRETER_CONFIGURATION_ID;
19 |
20 | @Tag("phpstan_by_interpreter")
21 | public class PhpStanRemoteConfiguration extends PhpStanConfiguration implements PhpSdkDependentConfiguration {
22 | private String myInterpreterId;
23 |
24 | @Override
25 | @Attribute("interpreter_id")
26 | public @Nullable @NlsSafe String getInterpreterId() {
27 | return myInterpreterId;
28 | }
29 |
30 | @Override
31 | public void setInterpreterId(@NotNull String interpreterId) {
32 | myInterpreterId = interpreterId;
33 | }
34 |
35 | @Override
36 | public @NotNull @NlsContexts.Label String getPresentableName(@Nullable Project project) {
37 | if (isCreatedAsDefaultInterpreterConfiguration()) return PhpBundle.message("quality.tools.label.by.default.project.interpreter");
38 | return getDefaultName(PhpInterpretersManagerImpl.getInstance(project).findInterpreterName(getInterpreterId()));
39 | }
40 |
41 | @Override
42 | public @NotNull @Nls String getId() {
43 | if (isCreatedAsDefaultInterpreterConfiguration()) return DEFAULT_INTERPRETER_CONFIGURATION_ID;
44 | final String interpreterId = getInterpreterId();
45 | return isEmpty(interpreterId) ? PhpStanBundle.message("undefined.interpreter") : interpreterId;
46 | }
47 |
48 | public static @NotNull @Nls String getDefaultName(@Nls @Nullable String interpreterName) {
49 | return isEmpty(interpreterName) ? PhpStanBundle.message("undefined.interpreter") : interpreterName;
50 | }
51 |
52 | @Override
53 | public PhpStanRemoteConfiguration clone() {
54 | PhpStanRemoteConfiguration settings = new PhpStanRemoteConfiguration();
55 | settings.myInterpreterId = myInterpreterId;
56 | settings.setCreatedAsDefaultInterpreterConfiguration(this.isCreatedAsDefaultInterpreterConfiguration());
57 | settings.setDeletedFromTheList(this.isDeletedFromTheList());
58 | clone(settings);
59 | return settings;
60 | }
61 |
62 | @Override
63 | public String serialize(@Nullable String path) {
64 | return path;
65 | }
66 |
67 | @Override
68 | public String deserialize(@Nullable String path) {
69 | return path;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/BUILD.bazel:
--------------------------------------------------------------------------------
1 | ### auto-generated section `build intellij.phpstan` start
2 | load("@rules_jvm//:jvm.bzl", "jvm_library", "resourcegroup")
3 |
4 | resourcegroup(
5 | name = "phpstan_resources",
6 | srcs = glob(["resources/**/*"]),
7 | strip_prefix = "resources"
8 | )
9 |
10 | resourcegroup(
11 | name = "phpstan_test_resources",
12 | srcs = glob(["testData/**/*"]),
13 | strip_prefix = "testData"
14 | )
15 |
16 | jvm_library(
17 | name = "phpstan",
18 | module_name = "intellij.phpstan",
19 | visibility = ["//visibility:public"],
20 | srcs = glob(["src/**/*.kt", "src/**/*.java", "src/**/*.form"], allow_empty = True),
21 | resources = [":phpstan_resources"],
22 | deps = [
23 | "//phpstorm/php:php-impl",
24 | "@community//platform/core-api:core",
25 | "@community//platform/analysis-impl",
26 | "@community//platform/editor-ui-api:editor-ui",
27 | "@community//platform/projectModel-api:projectModel",
28 | "@community//platform/analysis-api:analysis",
29 | "@community//platform/platform-api:ide",
30 | "@community//platform/remote-core",
31 | "@community//platform/util/jdom",
32 | "@community//libraries/gson",
33 | "@community//platform/core-impl",
34 | "//phpstorm/phpstorm-remote-interpreter:php-remoteInterpreter",
35 | "@community//platform/platform-impl:ide-impl",
36 | "@community//platform/core-ui",
37 | "//phpstorm/php-openapi:php",
38 | "@community//platform/platform-util-io:ide-util-io",
39 | "@community//platform/forms_rt:java-guiForms-rt",
40 | ]
41 | )
42 |
43 | jvm_library(
44 | name = "phpstan_test_lib",
45 | visibility = ["//visibility:public"],
46 | srcs = glob(["tests/**/*.kt", "tests/**/*.java", "tests/**/*.form"], allow_empty = True),
47 | resources = [":phpstan_test_resources"],
48 | associates = [":phpstan"],
49 | deps = [
50 | "//phpstorm/php:php-impl",
51 | "//phpstorm/php:php-impl_test_lib",
52 | "@community//platform/core-api:core",
53 | "@community//platform/analysis-impl",
54 | "@community//platform/editor-ui-api:editor-ui",
55 | "@community//platform/projectModel-api:projectModel",
56 | "@community//platform/analysis-api:analysis",
57 | "@community//platform/platform-api:ide",
58 | "@community//platform/remote-core",
59 | "@community//platform/testFramework",
60 | "@community//platform/testFramework:testFramework_test_lib",
61 | "@community//platform/util/jdom",
62 | "@community//libraries/gson",
63 | "@community//platform/core-impl",
64 | "//phpstorm/phpstorm-remote-interpreter:php-remoteInterpreter",
65 | "//phpstorm/phpstorm-remote-interpreter:php-remoteInterpreter_test_lib",
66 | "@community//platform/platform-impl:ide-impl",
67 | "@community//platform/core-ui",
68 | "//phpstorm/php-openapi:php",
69 | "@community//platform/platform-util-io:ide-util-io",
70 | "@community//platform/forms_rt:java-guiForms-rt",
71 | ]
72 | )
73 | ### auto-generated section `build intellij.phpstan` end
74 |
75 | ### auto-generated section `test intellij.phpstan` start
76 | load("@community//build:tests-options.bzl", "jps_test")
77 |
78 | jps_test(
79 | name = "phpstan_test",
80 | runtime_deps = [":phpstan_test_lib"]
81 | )
82 | ### auto-generated section `test intellij.phpstan` end
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.intellij.openapi.util.text.StringUtil;
4 | import com.intellij.util.xmlb.annotations.Attribute;
5 | import com.intellij.util.xmlb.annotations.Transient;
6 | import com.jetbrains.php.tools.quality.QualityToolConfiguration;
7 | import org.jetbrains.annotations.Nls;
8 | import org.jetbrains.annotations.NotNull;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | import static com.jetbrains.php.tools.quality.phpstan.PhpStanConfigurationManager.DEFAULT_MAX_MESSAGES_PER_FILE;
12 |
13 |
14 | /**
15 | * Stores configuration needed to run PHPStan in selected environment.
16 | */
17 | public class PhpStanConfiguration extends QualityToolConfiguration {
18 | private String myPhpStanPath = "";
19 | private int myMaxMessagesPerFile = DEFAULT_MAX_MESSAGES_PER_FILE;
20 | private int myTimeoutMs = 30000;
21 |
22 | @Override
23 | @Transient
24 | public String getToolPath() {
25 | return myPhpStanPath;
26 | }
27 |
28 | @Override
29 | public void setToolPath(String toolPath) {
30 | myPhpStanPath = toolPath;
31 | }
32 |
33 | @SuppressWarnings("UnusedDeclaration")
34 | @Attribute("tool_path")
35 | public @Nullable String getSerializedToolPath() {
36 | return serialize(myPhpStanPath);
37 | }
38 |
39 | @SuppressWarnings("UnusedDeclaration")
40 | public void setSerializedToolPath(@Nullable String configurationFilePath) {
41 | myPhpStanPath = deserialize(configurationFilePath);
42 | }
43 |
44 | @Override
45 | @Attribute("max_messages_per_file")
46 | public int getMaxMessagesPerFile() {
47 | return myMaxMessagesPerFile;
48 | }
49 |
50 | @Override
51 | @Attribute("timeout")
52 | public int getTimeout() {
53 | return myTimeoutMs;
54 | }
55 |
56 | @Override
57 | public void setTimeout(int timeout) {
58 | myTimeoutMs = timeout;
59 | }
60 |
61 | @Override
62 | public @NotNull @Nls String getId() {
63 | return PhpStanBundle.message("local");
64 | }
65 |
66 | @Override
67 | public @Nullable String getInterpreterId() {
68 | return null;
69 | }
70 |
71 | @Override
72 | public PhpStanConfiguration clone() {
73 | PhpStanConfiguration settings = new PhpStanConfiguration();
74 | clone(settings);
75 | return settings;
76 | }
77 |
78 | public PhpStanConfiguration clone(@NotNull PhpStanConfiguration settings) {
79 | settings.myPhpStanPath = myPhpStanPath;
80 | settings.myMaxMessagesPerFile = myMaxMessagesPerFile;
81 | settings.myTimeoutMs = myTimeoutMs;
82 | return settings;
83 | }
84 |
85 | @Override
86 | public int compareTo(@NotNull QualityToolConfiguration o) {
87 | if (!(o instanceof PhpStanConfiguration)) {
88 | return 1;
89 | }
90 |
91 | if (StringUtil.equals(getPresentableName(null), PhpStanBundle.message("label.system.php"))) {
92 | return -1;
93 | }
94 | else if (StringUtil.equals(o.getPresentableName(null), PhpStanBundle.message("label.system.php"))) {
95 | return 1;
96 | }
97 | return StringUtil.compare(getPresentableName(null), o.getPresentableName(null), false);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/testData/parser/Template.txt:
--------------------------------------------------------------------------------
1 |
8 | */
9 | function f() {
10 | }
11 |
12 | ---
13 | PHP file
14 | PsiElement(Non Lazy Group statement)
15 | PsiElement(php opening tag)('
34 | PsiWhiteSpace('\n ')
35 | PsiElement(DOC_LEADING_ASTERISK)('*')
36 | PsiWhiteSpace(' ')
37 | PhpDocTemplateTagImpl: @phpstan-template-covaria...
38 | PsiElement(DOC_TAG_NAME)('@phpstan-template-covariant')
39 | PsiWhiteSpace(' ')
40 | PhpDocTemplateParameterImpl: M
41 | PsiElement(DOC_IDENTIFIER)('M')
42 | PsiWhiteSpace(' ')
43 | PsiElement(DOC_IDENTIFIER)('of')
44 | PsiWhiteSpace(' ')
45 | PhpDocTypeImpl: F
46 | PsiElement(DOC_IDENTIFIER)('F')
47 | PsiWhiteSpace(' ')
48 | PhpPsiElementImpl
49 | PsiElement(DOC_IDENTIFIER)('Description')
50 | PsiWhiteSpace('\n ')
51 | PsiElement(DOC_LEADING_ASTERISK)('*')
52 | PsiWhiteSpace(' ')
53 | PhpDocTemplateTagImpl: @phpstan-template-contrav...
54 | PsiElement(DOC_TAG_NAME)('@phpstan-template-contravariant')
55 | PsiWhiteSpace(' ')
56 | PhpDocTemplateParameterImpl: L
57 | PsiElement(DOC_IDENTIFIER)('L')
58 | PhpPsiElementImpl
59 |
60 | PsiWhiteSpace('\n ')
61 | PsiElement(DOC_LEADING_ASTERISK)('*')
62 | PsiWhiteSpace(' ')
63 | PhpDocTagImpl: @phpstan
64 | PsiElement(DOC_TAG_NAME)('@phpstan')
65 | PsiWhiteSpace(' ')
66 | PhpPsiElementImpl
67 | PsiElement(DOC_IDENTIFIER)('T')
68 | PsiWhiteSpace('\n ')
69 | PsiElement(DOC_LEADING_ASTERISK)('*')
70 | PsiWhiteSpace(' ')
71 | PhpDocTagImpl: @phpstan-template-use
72 | PsiElement(DOC_TAG_NAME)('@phpstan-template-use')
73 | PsiWhiteSpace(' ')
74 | PhpDocTypeImpl: MyClass
75 | PsiElement(DOC_IDENTIFIER)('MyClass')
76 | PhpPsiElementImpl
77 | PsiElement(DOC_LAB)('<')
78 | PhpDocTypeImpl: T
79 | PsiElement(DOC_IDENTIFIER)('T')
80 | PsiElement(DOC_RAB)('>')
81 | PhpPsiElementImpl
82 |
83 | PsiWhiteSpace('\n ')
84 | PsiElement(DOC_COMMENT_END)('*/')
85 | PsiWhiteSpace('\n')
86 | FunctionImpl: f
87 | PsiElement(function)('function')
88 | PsiWhiteSpace(' ')
89 | PsiElement(identifier)('f')
90 | PsiElement(()('(')
91 | Parameter list
92 |
93 | PsiElement())(')')
94 | PsiWhiteSpace(' ')
95 | PsiElement(Group statement)
96 | PsiElement({)('{')
97 | PsiWhiteSpace('\n')
98 | PsiElement(})('}')
--------------------------------------------------------------------------------
/testData/parser/Type.txt:
--------------------------------------------------------------------------------
1 |
35 | PsiWhiteSpace('\n ')
36 | PsiElement(DOC_COMMENT_END)('*/')
37 | PsiWhiteSpace('\n')
38 | Statement
39 | AssignmentExpressionImpl: $foo = 1
40 | VariableImpl: foo
41 | PsiElement(variable)('$foo')
42 | PsiWhiteSpace(' ')
43 | PsiElement(assign)('=')
44 | PsiWhiteSpace(' ')
45 | PhpExpressionImpl: 1
46 | PsiElement(integer)('1')
47 | PsiElement(semicolon)(';')
48 | PsiWhiteSpace('\n')
49 | PhpDocCommentImpl
50 | PsiElement(DOC_COMMENT_START)('/**')
51 | PsiWhiteSpace('\n ')
52 | PsiElement(DOC_LEADING_ASTERISK)('*')
53 | PsiWhiteSpace(' ')
54 | PhpDocTagImpl: @phpstan-check-type
55 | PsiElement(DOC_TAG_NAME)('@phpstan-check-type')
56 | PsiWhiteSpace(' ')
57 | PhpDocVarImpl: bar
58 | PsiElement(DOC_VARIABLE)('$bar')
59 | PsiWhiteSpace(' ')
60 | PsiElement(DOC_TEXT)('=')
61 | PsiWhiteSpace(' ')
62 | PhpDocTypeImpl: int
63 | PsiElement(DOC_IDENTIFIER)('int')
64 | PhpPsiElementImpl
65 |
66 | PsiWhiteSpace('\n ')
67 | PsiElement(DOC_LEADING_ASTERISK)('*')
68 | PsiWhiteSpace(' ')
69 | PhpDocTagImpl: @phpstan-type
70 | PsiElement(DOC_TAG_NAME)('@phpstan-type')
71 | PsiWhiteSpace(' ')
72 | PsiElement(DOC_IDENTIFIER)('PhoneType')
73 | PsiWhiteSpace(' ')
74 | PsiElement(DOC_TEXT)('=')
75 | PsiWhiteSpace(' ')
76 | PhpDocTypeImpl: array
77 | PsiElement(DOC_IDENTIFIER)('array')
78 | PhpPsiElementImpl
79 | PsiElement(DOC_LBRACE)('{')
80 | PsiElement(DOC_IDENTIFIER)('phone')
81 | PsiElement(DOC_TEXT)(':')
82 | PsiWhiteSpace(' ')
83 | PhpDocTypeImpl: string
84 | PsiElement(DOC_IDENTIFIER)('string')
85 | PsiElement(DOC_RBRACE)('}')
86 | PhpPsiElementImpl
87 |
88 | PsiWhiteSpace('\n ')
89 | PsiElement(DOC_COMMENT_END)('*/')
90 | PsiWhiteSpace('\n')
91 | Statement
92 | AssignmentExpressionImpl: $bar = 1
93 | VariableImpl: bar
94 | PsiElement(variable)('$bar')
95 | PsiWhiteSpace(' ')
96 | PsiElement(assign)('=')
97 | PsiWhiteSpace(' ')
98 | PhpExpressionImpl: 1
99 | PsiElement(integer)('1')
100 | PsiElement(semicolon)(';')
--------------------------------------------------------------------------------
/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | PHPStan Support
3 | com.intellij.php.tools.quality.phpstan
4 | Plugin provides PHPStan static analysis tool support
5 | JetBrains
6 | com.jetbrains.php
7 | com.intellij.modules.ultimate
8 | PHP Tools
9 | messages.PhpStanBundle
10 | org.jetbrains.plugins.phpstorm-remote-interpreter
11 |
12 |
14 |
15 |
16 |
17 |
18 |
20 |
27 |
28 |
29 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
47 |
48 |
49 |
50 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanQualityToolType.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.intellij.codeInspection.InspectionProfile;
4 | import com.intellij.codeInspection.ex.InspectionToolWrapper;
5 | import com.intellij.openapi.options.Configurable;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
8 | import com.jetbrains.php.tools.quality.*;
9 | import org.jetbrains.annotations.NotNull;
10 | import org.jetbrains.annotations.Nullable;
11 |
12 | import static com.intellij.util.ObjectUtils.tryCast;
13 | import static com.jetbrains.php.tools.quality.phpstan.PhpStanConfigurationBaseManager.PHP_STAN;
14 |
15 | public final class PhpStanQualityToolType extends QualityToolType {
16 | public static final PhpStanQualityToolType INSTANCE = new PhpStanQualityToolType();
17 |
18 | private PhpStanQualityToolType() {
19 | }
20 |
21 | @Override
22 | public @NotNull String getDisplayName() {
23 | return PHP_STAN;
24 | }
25 |
26 | @Override
27 | public @NotNull QualityToolBlackList getQualityToolBlackList(@NotNull Project project) {
28 | return PhpStanBlackList.getInstance(project);
29 | }
30 |
31 | @Override
32 | public @NotNull QualityToolConfigurationManager getConfigurationManager(@NotNull Project project) {
33 | return PhpStanConfigurationManager.getInstance(project);
34 | }
35 |
36 | @Override
37 | protected @NotNull PhpStanValidationInspection getInspection() {
38 | return new PhpStanValidationInspection();
39 | }
40 |
41 | @Override
42 | protected @Nullable QualityToolConfigurationProvider getConfigurationProvider() {
43 | return PhpStanConfigurationProvider.getInstances();
44 | }
45 |
46 | @Override
47 | protected @NotNull QualityToolConfigurableForm createConfigurableForm(@NotNull Project project,
48 | PhpStanConfiguration settings) {
49 | return new PhpStanConfigurableForm<>(project, settings);
50 | }
51 |
52 | @Override
53 | protected @NotNull Configurable getToolConfigurable(@NotNull Project project) {
54 | return new PhpStanConfigurable(project);
55 | }
56 |
57 | @Override
58 | protected @NotNull QualityToolProjectConfiguration getProjectConfiguration(@NotNull Project project) {
59 | return PhpStanProjectConfiguration.getInstance(project);
60 | }
61 |
62 | @Override
63 | protected @NotNull PhpStanConfiguration createConfiguration() {
64 | return new PhpStanConfiguration();
65 | }
66 |
67 | @Override
68 | public @NotNull String getHelpTopic() {
69 | return "reference.settings.php.PHPStan";
70 | }
71 |
72 | @Override
73 | public QualityToolValidationGlobalInspection getGlobalTool(@NotNull Project project,
74 | @Nullable InspectionProfile profile) {
75 | if (profile == null) {
76 | profile = InspectionProjectProfileManager.getInstance(project).getCurrentProfile();
77 | }
78 | final InspectionToolWrapper, ?> inspectionTool = profile.getInspectionTool(getInspectionId(), project);
79 | if (inspectionTool == null) {
80 | return null;
81 | }
82 | return tryCast(inspectionTool.getTool(), PhpStanGlobalInspection.class);
83 | }
84 |
85 | @Override
86 | public String getInspectionId() {
87 | return "PhpStanGlobal";
88 | }
89 |
90 | @Override
91 | public String getInspectionShortName(@NotNull Project project) {
92 | final QualityToolValidationGlobalInspection tool = getGlobalTool(project, null);
93 | if (tool != null) {
94 | return tool.getShortName();
95 | }
96 | return getInspection().getShortName();
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/tests/integration/tests/phpstan/PhpStanNewTagsTest.kt:
--------------------------------------------------------------------------------
1 | package integration.tests.phpstan
2 |
3 | import com.intellij.openapi.application.PathManager
4 | import com.intellij.openapi.util.io.FileUtil
5 | import com.intellij.psi.util.childrenOfType
6 | import com.intellij.testFramework.UsefulTestCase
7 | import com.jetbrains.php.PhpIndex
8 | import com.jetbrains.php.fixtures.PhpCodeInsightFixtureTestCase
9 | import com.jetbrains.php.lang.psi.elements.impl.ArrayCreationExpressionImpl
10 | import com.jetbrains.php.lang.psi.elements.impl.ForeachImpl
11 | import com.jetbrains.php.phpstan.lang.documentation.parser.PhpStanDocParserTest
12 | import java.io.BufferedInputStream
13 | import java.io.BufferedOutputStream
14 | import java.io.File
15 | import java.io.FileOutputStream
16 | import java.net.URL
17 | import java.util.zip.ZipInputStream
18 | import kotlin.io.path.div
19 |
20 | class PhpStanNewTagsTest : PhpCodeInsightFixtureTestCase() {
21 |
22 | private lateinit var phpstanFolder: File
23 | override fun getFixtureTestDataFolder(): String {
24 | return "newTags"
25 | }
26 |
27 | override fun getTestDataHome(): String {
28 | return PhpStanDocParserTest.TEST_DATA_HOME
29 | }
30 |
31 |
32 | override fun setUp() {
33 | super.setUp()
34 | phpstanFolder = File(PathManager.getHomePath() + "/" + basePath)
35 | FileUtil.createDirectory(phpstanFolder)
36 | val zipUrl = URL("https://github.com/phpstan/phpstan-src/archive/refs/heads/1.10.x.zip")
37 | downloadAndUnpackZip(zipUrl, phpstanFolder)
38 | }
39 |
40 | fun testNewTags() {
41 | myFixture.copyFileToProject("phpstan-src-1.10.x/src/PhpDoc/PhpDocNodeResolver.php")
42 | val classWithAnnotations = PhpIndex.getInstance(project).getClassesByFQN("PHPStan\\PhpDoc\\PhpDocNodeResolver").first()
43 | val annotations = mutableListOf()
44 | classWithAnnotations.methods.forEach { method ->
45 | method.lastChild.childrenOfType().forEach{ forEach ->
46 | if(forEach.array is ArrayCreationExpressionImpl){
47 | (forEach.array as ArrayCreationExpressionImpl).values().forEach {
48 | annotations.add(it.text)
49 | }
50 | }
51 | }
52 | }
53 | val resultFileName = "phpstan-tags.txt"
54 | val resultFile = (phpstanFolder.toPath() / resultFileName).toFile()
55 | resultFile.writeText(annotations.sorted().joinToString("\n"))
56 | println("##teamcity[publishArtifacts '${resultFile.absolutePath}']")
57 | val pathToPreviousResults = (phpstanFolder.toPath() / "previousResults" / resultFileName).toFile()
58 | if(pathToPreviousResults.exists()){
59 | UsefulTestCase.assertSameElements(annotations, pathToPreviousResults.readLines())
60 | } else {
61 | println("Previous results are not found at ${pathToPreviousResults.absolutePath}")
62 | }
63 | }
64 | private fun downloadAndUnpackZip(zipUrl: URL, destinationDirectory: File) {
65 | val zipConnection = zipUrl.openConnection()
66 | val zipInputStream = ZipInputStream(BufferedInputStream(zipConnection.getInputStream()))
67 | var zipEntry = zipInputStream.nextEntry
68 | while (zipEntry != null) {
69 | val file = File(destinationDirectory, zipEntry.name)
70 | if (zipEntry.isDirectory) {
71 | file.mkdirs()
72 | }
73 | else {
74 | val fileOutputStream = FileOutputStream(file)
75 | val bufferedOutputStream = BufferedOutputStream(fileOutputStream, 2048)
76 | val data = ByteArray(2048)
77 | var count: Int
78 | while (zipInputStream.read(data, 0, data.size).also { count = it } != -1) {
79 | bufferedOutputStream.write(data, 0, count)
80 | }
81 | bufferedOutputStream.flush()
82 | bufferedOutputStream.close()
83 | }
84 | zipInputStream.closeEntry()
85 | zipEntry = zipInputStream.nextEntry
86 | }
87 | zipInputStream.close()
88 | }
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanAnnotatorProxy.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.intellij.codeInspection.InspectionProfile;
4 | import com.intellij.openapi.project.Project;
5 | import com.intellij.openapi.roots.ProjectRootManager;
6 | import com.intellij.openapi.vfs.VirtualFile;
7 | import com.intellij.psi.PsiFile;
8 | import com.intellij.util.SmartList;
9 | import com.jetbrains.php.tools.quality.QualityToolAnnotator;
10 | import com.jetbrains.php.tools.quality.QualityToolAnnotatorInfo;
11 | import com.jetbrains.php.tools.quality.QualityToolConfiguration;
12 | import com.jetbrains.php.tools.quality.QualityToolMessageProcessor;
13 | import org.jetbrains.annotations.NotNull;
14 | import org.jetbrains.annotations.Nullable;
15 |
16 | import java.util.List;
17 |
18 | import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
19 | import static com.intellij.util.containers.ContainerUtil.*;
20 | import static java.util.Collections.singletonList;
21 |
22 | public final class PhpStanAnnotatorProxy extends QualityToolAnnotator {
23 | public static final PhpStanAnnotatorProxy INSTANCE = new PhpStanAnnotatorProxy();
24 |
25 | @Override
26 | protected List getOptions(@Nullable String filePath, @NotNull PhpStanValidationInspection inspection,
27 | @Nullable InspectionProfile profile, @NotNull Project project) {
28 | return emptyList();
29 | }
30 |
31 | @Override
32 | protected @Nullable List getOptions(@Nullable String filePath,
33 | @NotNull PhpStanValidationInspection inspection,
34 | @Nullable InspectionProfile profile,
35 | @NotNull Project project,
36 | boolean isOnTheFly) {
37 | final PhpStanGlobalInspection tool = (PhpStanGlobalInspection)getQualityToolType().getGlobalTool(project, profile);
38 | if (tool == null) {
39 | return emptyList();
40 | }
41 |
42 | if (isOnTheFly) {
43 | return tool.getCommandLineOptions(singletonList(filePath), project);
44 | }
45 | PhpStanOptionsConfiguration configuration = PhpStanOptionsConfiguration.getInstance(project);
46 | return tool.getCommandLineOptions(configuration.isFullProject()
47 | ? new SmartList<>(filePath, project.getBasePath())
48 | : isNotEmpty(configuration.getConfig()) ? emptyList() : concat(map(
49 | ProjectRootManager.getInstance(project).getContentSourceRoots(),
50 | VirtualFile::getPath)), project);
51 | }
52 |
53 | @Override
54 | protected QualityToolMessageProcessor createMessageProcessor(@NotNull QualityToolAnnotatorInfo collectedInfo) {
55 | return new PhpStanMessageProcessor(collectedInfo);
56 | }
57 |
58 | @Override
59 | protected @NotNull QualityToolAnnotatorInfo createAnnotatorInfo(@Nullable PsiFile file,
60 | PhpStanValidationInspection tool,
61 | InspectionProfile inspectionProfile,
62 | Project project,
63 | QualityToolConfiguration configuration,
64 | boolean isOnTheFly) {
65 | return new PhpStanQualityToolAnnotatorInfo(file, tool, inspectionProfile, project, configuration, isOnTheFly);
66 | }
67 |
68 | @Override
69 | protected @NotNull PhpStanQualityToolType getQualityToolType() {
70 | return PhpStanQualityToolType.INSTANCE;
71 | }
72 |
73 | @Override
74 | public String getPairedBatchInspectionShortName() {
75 | return getQualityToolType().getInspectionId();
76 | }
77 |
78 | @Override
79 | protected boolean showMessage(@NotNull String message) {
80 | return !message.contains("The Xdebug PHP extension is active, but \"--xdebug\" is not used");
81 | }
82 | }
83 |
84 |
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/remote/PhpStanRemoteConfigurationProvider.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan.remote;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.util.text.StringUtil;
5 | import com.intellij.util.NullableFunction;
6 | import com.intellij.util.xmlb.XmlSerializer;
7 | import com.jetbrains.php.config.interpreters.PhpInterpreter;
8 | import com.jetbrains.php.config.interpreters.PhpInterpretersManagerImpl;
9 | import com.jetbrains.php.config.interpreters.PhpSdkAdditionalData;
10 | import com.jetbrains.php.remote.interpreter.PhpRemoteSdkAdditionalData;
11 | import com.jetbrains.php.remote.tools.quality.QualityToolByInterpreterConfigurableForm;
12 | import com.jetbrains.php.remote.tools.quality.QualityToolByInterpreterDialog;
13 | import com.jetbrains.php.tools.quality.QualityToolConfigurableForm;
14 | import com.jetbrains.php.tools.quality.phpstan.*;
15 | import org.jdom.Element;
16 | import org.jetbrains.annotations.NonNls;
17 | import org.jetbrains.annotations.NotNull;
18 | import org.jetbrains.annotations.Nullable;
19 |
20 | import java.util.List;
21 |
22 | import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
23 | import static com.jetbrains.php.remote.tools.quality.QualityToolByInterpreterDialog.getLocalOrDefaultInterpreterConfiguration;
24 | import static com.jetbrains.php.tools.quality.phpstan.PhpStanConfigurationBaseManager.PHP_STAN;
25 |
26 | public class PhpStanRemoteConfigurationProvider extends PhpStanConfigurationProvider {
27 |
28 | private static final @NonNls String PHPSTAN_BY_INTERPRETER = "phpstan_by_interpreter";
29 |
30 | @Override
31 | public boolean canLoad(@NotNull String tagName) {
32 | return StringUtil.equals(tagName, PHPSTAN_BY_INTERPRETER);
33 | }
34 |
35 | @Override
36 | public @Nullable PhpStanConfiguration load(@NotNull Element element) {
37 | return XmlSerializer.deserialize(element, PhpStanRemoteConfiguration.class);
38 | }
39 |
40 | @Override
41 | public @Nullable QualityToolConfigurableForm createConfigurationForm(@NotNull Project project,
42 | @NotNull PhpStanConfiguration settings) {
43 | if (settings instanceof PhpStanRemoteConfiguration remoteConfiguration) {
44 | final PhpStanConfigurableForm delegate =
45 | new PhpStanConfigurableForm<>(project, remoteConfiguration);
46 | return new QualityToolByInterpreterConfigurableForm<>(project, remoteConfiguration, delegate);
47 | }
48 | return null;
49 | }
50 |
51 | @Override
52 | public PhpStanConfiguration createNewInstance(@Nullable Project project, @NotNull List existingSettings) {
53 | var dialog =
54 | new QualityToolByInterpreterDialog<>(project, existingSettings, PHP_STAN, PhpStanConfiguration.class, PhpStanQualityToolType.INSTANCE);
55 | if (dialog.showAndGet()) {
56 | final String id = PhpInterpretersManagerImpl.getInstance(project).findInterpreterId(dialog.getSelectedInterpreterName());
57 | if (isNotEmpty(id)) {
58 | final PhpStanRemoteConfiguration settings = new PhpStanRemoteConfiguration();
59 | settings.setInterpreterId(id);
60 |
61 | final PhpSdkAdditionalData data = PhpInterpretersManagerImpl.getInstance(project).findInterpreterDataById(id);
62 | fillDefaultSettings(project, settings, PhpStanConfigurationManager.getInstance(project).getOrCreateLocalSettings(), data, data instanceof PhpRemoteSdkAdditionalData);
63 |
64 | return settings;
65 | }
66 | return (PhpStanConfiguration)getLocalOrDefaultInterpreterConfiguration(dialog.getSelectedInterpreterName(), project, PhpStanQualityToolType.INSTANCE);
67 | }
68 | return null;
69 | }
70 |
71 | @Override
72 | public PhpStanConfiguration createConfigurationByInterpreter(@NotNull PhpInterpreter interpreter) {
73 | final PhpStanRemoteConfiguration settings = new PhpStanRemoteConfiguration();
74 | settings.setInterpreterId(interpreter.getId());
75 | return settings;
76 | }
77 |
78 | @Override
79 | protected void fillSettingsByDefaultValue(@NotNull PhpStanConfiguration settings,
80 | @NotNull PhpStanConfiguration localConfiguration,
81 | @NotNull NullableFunction preparePath) {
82 | super.fillSettingsByDefaultValue(settings, localConfiguration, preparePath);
83 | settings.setTimeout(60000);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanGlobalInspection.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.intellij.codeInspection.*;
4 | import com.intellij.codeInspection.ex.ExternalAnnotatorBatchInspection;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.openapi.util.Key;
7 | import com.intellij.openapi.util.NlsSafe;
8 | import com.intellij.psi.PsiFile;
9 | import com.intellij.util.containers.ContainerUtil;
10 | import com.jetbrains.php.tools.quality.QualityToolAnnotatorInfo;
11 | import com.jetbrains.php.tools.quality.QualityToolValidationGlobalInspection;
12 | import com.jetbrains.php.tools.quality.QualityToolXmlMessageProcessor;
13 | import org.jetbrains.annotations.NonNls;
14 | import org.jetbrains.annotations.NotNull;
15 | import org.jetbrains.annotations.Nullable;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 | import java.util.Objects;
20 |
21 | import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
22 | import static com.jetbrains.php.tools.quality.QualityToolAnnotator.updateIfRemoteMappingExists;
23 |
24 | public class PhpStanGlobalInspection extends QualityToolValidationGlobalInspection implements ExternalAnnotatorBatchInspection {
25 | public boolean FULL_PROJECT = false;
26 | public @NonNls String memoryLimit = "2G";
27 | public int level = 4;
28 | public @NlsSafe String config = "";
29 | public @NlsSafe String autoload = "";
30 | public static final Key> PHPSTAN_ANNOTATOR_INFO = Key.create("ANNOTATOR_INFO_2");
31 |
32 | @Override
33 | public void inspectionStarted(@NotNull InspectionManager manager,
34 | @NotNull GlobalInspectionContext globalContext,
35 | @NotNull ProblemDescriptionsProcessor problemDescriptionsProcessor) {
36 | super.inspectionStarted(manager, globalContext, problemDescriptionsProcessor);
37 | final PhpStanAnnotatorProxy annotator = getAnnotator();
38 | final QualityToolAnnotatorInfo info =
39 | annotator.collectAnnotatorInfo(null, null, globalContext.getProject(), ((InspectionManagerBase)manager).getCurrentProfile(), false);
40 | if (info != null) {
41 | manager.getProject().putUserData(ANNOTATOR_INFO, annotator.doAnnotate(info));
42 | }
43 | }
44 |
45 | @Override
46 | public @Nullable LocalInspectionTool getSharedLocalInspectionTool() {
47 | return new PhpStanValidationInspection();
48 | }
49 |
50 | @Override
51 | protected @NotNull PhpStanAnnotatorProxy getAnnotator() {
52 | return PhpStanAnnotatorProxy.INSTANCE;
53 | }
54 |
55 | @Override
56 | protected Key> getKey() {
57 | return PHPSTAN_ANNOTATOR_INFO;
58 | }
59 |
60 | public List getCommandLineOptions(@NotNull List filePath, @NotNull Project project) {
61 | @NonNls ArrayList options = new ArrayList<>();
62 | PhpStanOptionsConfiguration configuration = PhpStanOptionsConfiguration.getInstance(project);
63 | options.add("analyze");
64 | if (isNotEmpty(configuration.getConfig())) {
65 | options.add("-c");
66 | options.add(updateIfRemoteMappingExists(configuration.getConfig(), project, PhpStanQualityToolType.INSTANCE));
67 | }
68 | else {
69 | options.add("--level=" + configuration.getLevel());
70 | }
71 | if (isNotEmpty(configuration.getAutoload())) {
72 | options.add("-a");
73 | options.add(updateIfRemoteMappingExists(configuration.getAutoload(), project, PhpStanQualityToolType.INSTANCE));
74 | }
75 | options.add("--memory-limit=" + configuration.getMemoryLimit());
76 | options.add("--error-format=checkstyle");
77 | options.add("--no-progress");
78 | options.add("--no-ansi");
79 | options.add("--no-interaction");
80 | List filePaths = ContainerUtil.filter(filePath, Objects::nonNull);
81 | filePaths = ContainerUtil.map(filePaths, it -> updateIfRemoteMappingExists(it, project, PhpStanQualityToolType.INSTANCE));
82 | options.addAll(filePaths);
83 | return options;
84 | }
85 |
86 | @Override
87 | public ProblemDescriptor @NotNull [] checkFile(@NotNull PsiFile file,
88 | @NotNull GlobalInspectionContext context,
89 | @NotNull InspectionManager manager) {
90 | ProblemsHolder holder = new ProblemsHolder(manager, file, false);
91 | super.checkFile(file, manager, holder, context, null);
92 | return holder.getResultsArray();
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanOptionsPanel.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.util.text.StringUtil;
5 | import com.intellij.ui.DocumentAdapter;
6 | import com.intellij.ui.JBIntSpinner;
7 | import com.intellij.ui.components.JBCheckBox;
8 | import com.intellij.ui.components.JBTextField;
9 | import com.jetbrains.php.config.interpreters.PhpInterpreter;
10 | import com.jetbrains.php.config.interpreters.PhpTextFieldWithSdkBasedBrowse;
11 | import com.jetbrains.php.tools.quality.QualityToolConfigurationComboBox;
12 | import com.jetbrains.php.tools.quality.QualityToolsOptionsPanel;
13 | import org.jetbrains.annotations.NotNull;
14 | import org.jetbrains.annotations.Nullable;
15 |
16 | import javax.swing.*;
17 | import javax.swing.event.DocumentEvent;
18 | import java.net.URL;
19 |
20 | import static com.intellij.openapi.vfs.VfsUtil.findFileByURL;
21 | import static com.intellij.openapi.vfs.VfsUtilCore.convertToURL;
22 | import static com.intellij.openapi.vfs.VfsUtilCore.pathToUrl;
23 |
24 | public class PhpStanOptionsPanel extends QualityToolsOptionsPanel {
25 | private JPanel myOptionsPanel;
26 | private JBCheckBox myFullProjectRunJBCheckBox;
27 | private JBTextField myMemoryLimitTextField;
28 | private JBIntSpinner myJBIntSpinner;
29 | private PhpTextFieldWithSdkBasedBrowse myConfigPathTextField;
30 | private PhpTextFieldWithSdkBasedBrowse myAutoloadPathTextField;
31 | private final QualityToolConfigurationComboBox myComboBox;
32 |
33 | public PhpStanOptionsPanel(Project project,
34 | QualityToolConfigurationComboBox comboBox,
35 | Runnable validate) {
36 | super(project, validate, PhpStanQualityToolType.INSTANCE);
37 | myComboBox = comboBox;
38 | PhpStanOptionsConfiguration configuration = PhpStanOptionsConfiguration.getInstance(project);
39 | myFullProjectRunJBCheckBox.setSelected(configuration.isFullProject());
40 | myMemoryLimitTextField.setText(configuration.getMemoryLimit());
41 | myJBIntSpinner.setNumber(configuration.getLevel());
42 | myConfigPathTextField.setText(configuration.getConfig());
43 | myConfigPathTextField
44 | .init(project, getSdkAdditionalData(project, comboBox), PhpStanBundle.message("phpstan.configuration.file"), true, false);
45 | myAutoloadPathTextField.setText(configuration.getAutoload());
46 | myAutoloadPathTextField
47 | .init(project, getSdkAdditionalData(project, comboBox), PhpStanBundle.message("phpstan.autoload.file"), true, false);
48 | myConfigPathTextField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
49 | @Override
50 | protected void textChanged(@NotNull DocumentEvent e) {
51 | validate.run();
52 | }
53 | });
54 | }
55 |
56 | private void createUIComponents() {
57 | myJBIntSpinner = new JBIntSpinner(4, 0, 8);
58 | }
59 |
60 | @Override
61 | public JPanel getOptionsPanel() {
62 | return myOptionsPanel;
63 | }
64 |
65 | @Override
66 | public void reset() {
67 | PhpStanOptionsConfiguration configuration = PhpStanOptionsConfiguration.getInstance(myProject);
68 | myFullProjectRunJBCheckBox.setSelected(configuration.isFullProject());
69 | myMemoryLimitTextField.setText(configuration.getMemoryLimit());
70 | myJBIntSpinner.setNumber(configuration.getLevel());
71 | myConfigPathTextField.setText(configuration.getConfig());
72 | myAutoloadPathTextField.setText(configuration.getAutoload());
73 | }
74 |
75 | @Override
76 | public boolean isModified() {
77 | PhpStanOptionsConfiguration configuration = PhpStanOptionsConfiguration.getInstance(myProject);
78 | if (myFullProjectRunJBCheckBox.isSelected() != configuration.isFullProject()) return true;
79 | if (!StringUtil.equals(myMemoryLimitTextField.getText(), configuration.getMemoryLimit())) return true;
80 | if (myJBIntSpinner.getNumber() != configuration.getLevel()) return true;
81 | if (!StringUtil.equals(myConfigPathTextField.getText(), configuration.getConfig())) return true;
82 | if (!StringUtil.equals(myAutoloadPathTextField.getText(), configuration.getAutoload())) return true;
83 | return false;
84 | }
85 |
86 | @Override
87 | public void apply() {
88 | PhpStanOptionsConfiguration configuration = PhpStanOptionsConfiguration.getInstance(myProject);
89 | configuration.setFullProject(myFullProjectRunJBCheckBox.isSelected());
90 | configuration.setMemoryLimit(myMemoryLimitTextField.getText());
91 | configuration.setLevel(myJBIntSpinner.getNumber());
92 | configuration.setConfig(myConfigPathTextField.getText());
93 | configuration.setAutoload(myAutoloadPathTextField.getText());
94 | }
95 |
96 | @Override
97 | protected @Nullable String validatePath() {
98 | PhpInterpreter interpreter = getSelectedInterpreter(myProject, myComboBox);
99 | if (interpreter != null && interpreter.isRemote()) {
100 | //TODO: validate remote path?
101 | return null;
102 | }
103 | final URL url = convertToURL(pathToUrl(myConfigPathTextField.getText()));
104 | if (url == null || findFileByURL(url) == null) {
105 | return PhpStanBundle.message("config.file.doesnt.exist");
106 | }
107 | return null;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/testData/parser/DocTags.txt:
--------------------------------------------------------------------------------
1 |
39 | PsiWhiteSpace('\n ')
40 | PsiElement(DOC_LEADING_ASTERISK)('*')
41 | PsiWhiteSpace(' ')
42 | PhpDocReturnTagImpl: @phpstan-return
43 | PsiElement(DOC_TAG_NAME)('@phpstan-return')
44 | PsiWhiteSpace(' ')
45 | PhpDocTypeImpl: string
46 | PsiElement(DOC_IDENTIFIER)('string')
47 | PhpPsiElementImpl
48 |
49 | PsiWhiteSpace('\n ')
50 | PsiElement(DOC_LEADING_ASTERISK)('*')
51 | PsiWhiteSpace(' ')
52 | PhpDocThrowsTagImpl: @phpstan-throws
53 | PsiElement(DOC_TAG_NAME)('@phpstan-throws')
54 | PsiWhiteSpace(' ')
55 | PhpDocTypeImpl: A
56 | PsiElement(DOC_IDENTIFIER)('A')
57 | PhpPsiElementImpl
58 |
59 | PsiWhiteSpace('\n ')
60 | PsiElement(DOC_LEADING_ASTERISK)('*')
61 | PsiWhiteSpace(' ')
62 | PhpDocTagImpl: @phpstan-use
63 | PsiElement(DOC_TAG_NAME)('@phpstan-use')
64 | PsiWhiteSpace(' ')
65 | PhpDocTypeImpl: A
66 | PsiElement(DOC_IDENTIFIER)('A')
67 | PhpPsiElementImpl
68 |
69 | PsiWhiteSpace('\n ')
70 | PsiElement(DOC_LEADING_ASTERISK)('*')
71 | PsiWhiteSpace(' ')
72 | PhpDocTagImpl: @phpstan-implements
73 | PsiElement(DOC_TAG_NAME)('@phpstan-implements')
74 | PsiWhiteSpace(' ')
75 | PhpDocTypeImpl: A
76 | PsiElement(DOC_IDENTIFIER)('A')
77 | PhpPsiElementImpl
78 |
79 | PsiWhiteSpace('\n ')
80 | PsiElement(DOC_LEADING_ASTERISK)('*')
81 | PsiWhiteSpace(' ')
82 | PhpDocTagImpl: @phpstan-extends
83 | PsiElement(DOC_TAG_NAME)('@phpstan-extends')
84 | PsiWhiteSpace(' ')
85 | PhpDocTypeImpl: A
86 | PsiElement(DOC_IDENTIFIER)('A')
87 | PhpPsiElementImpl
88 |
89 | PsiWhiteSpace('\n ')
90 | PsiElement(DOC_LEADING_ASTERISK)('*')
91 | PsiWhiteSpace(' ')
92 | PhpDocParamTagImpl: @phpstan-param-out
93 | PsiElement(DOC_TAG_NAME)('@phpstan-param-out')
94 | PsiWhiteSpace(' ')
95 | PhpDocTypeImpl: int
96 | PsiElement(DOC_IDENTIFIER)('int')
97 | PsiWhiteSpace(' ')
98 | PhpDocVarImpl: a
99 | PsiElement(DOC_VARIABLE)('$a')
100 | PhpPsiElementImpl
101 |
102 | PsiWhiteSpace('\n ')
103 | PsiElement(DOC_LEADING_ASTERISK)('*')
104 | PsiWhiteSpace(' ')
105 | PhpDocParamTagImpl: @param-out
106 | PsiElement(DOC_TAG_NAME)('@param-out')
107 | PsiWhiteSpace(' ')
108 | PhpDocTypeImpl: string
109 | PsiElement(DOC_IDENTIFIER)('string')
110 | PsiWhiteSpace(' ')
111 | PhpDocVarImpl: s
112 | PsiElement(DOC_VARIABLE)('$s')
113 | PhpPsiElementImpl
114 |
115 | PsiWhiteSpace('\n ')
116 | PsiElement(DOC_LEADING_ASTERISK)('*')
117 | PsiWhiteSpace(' ')
118 | PhpDocTagImpl: @phpstan-scope-this
119 | PsiElement(DOC_TAG_NAME)('@phpstan-scope-this')
120 | PsiWhiteSpace(' ')
121 | PhpDocTypeImpl: Exception
122 | PsiElement(DOC_IDENTIFIER)('Exception')
123 | PhpPsiElementImpl
124 |
125 | PsiWhiteSpace('\n ')
126 | PsiElement(DOC_LEADING_ASTERISK)('*')
127 | PsiWhiteSpace(' ')
128 | PhpDocParamTagImpl: @phpstan-param
129 | PsiElement(DOC_TAG_NAME)('@phpstan-param')
130 | PsiWhiteSpace(' ')
131 | PhpDocTypeImpl: %EMPTY%
132 | PsiElement(DOC_TEXT)('!')
133 | PsiElement(DOC_IDENTIFIER)('null')
134 | PsiWhiteSpace(' ')
135 | PhpDocVarImpl: c
136 | PsiElement(DOC_VARIABLE)('$c')
137 | PhpPsiElementImpl
138 |
139 | PsiWhiteSpace('\n ')
140 | PsiElement(DOC_COMMENT_END)('*/')
141 | PsiWhiteSpace('\n')
142 | FunctionImpl: a
143 | PsiElement(function)('function')
144 | PsiWhiteSpace(' ')
145 | PsiElement(identifier)('a')
146 | PsiElement(()('(')
147 | Parameter list
148 |
149 | PsiElement())(')')
150 | PsiWhiteSpace(' ')
151 | PsiElement(Group statement)
152 | PsiElement({)('{')
153 | PsiWhiteSpace('\n')
154 | PhpDocCommentImpl
155 | PsiElement(DOC_COMMENT_START)('/**')
156 | PsiWhiteSpace(' ')
157 | PhpDocParamTagImpl: @phpstan-var
158 | PsiElement(DOC_TAG_NAME)('@phpstan-var')
159 | PsiWhiteSpace(' ')
160 | PhpDocTypeImpl: int
161 | PsiElement(DOC_IDENTIFIER)('int')
162 | PsiWhiteSpace(' ')
163 | PhpDocVarImpl: a
164 | PsiElement(DOC_VARIABLE)('$a')
165 | PhpPsiElementImpl
166 |
167 | PsiWhiteSpace(' ')
168 | PsiElement(DOC_COMMENT_END)('*/')
169 | PsiWhiteSpace('\n')
170 | Echo
171 | PsiElement(echo)('echo')
172 | PsiWhiteSpace(' ')
173 | VariableImpl: %EMPTY%
174 | PsiElement(dollar)('$')
175 | PsiErrorElement:Expected: identifier
176 |
177 | PsiElement(semicolon)(';')
178 | PsiWhiteSpace('\n')
179 | PsiElement(})('}')
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanMessageProcessor.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.intellij.codeHighlighting.HighlightDisplayLevel;
4 | import com.intellij.openapi.application.ReadAction;
5 | import com.intellij.openapi.editor.Document;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.util.TextRange;
8 | import com.intellij.psi.PsiDocumentManager;
9 | import com.intellij.psi.PsiFile;
10 | import com.intellij.util.PathUtil;
11 | import com.jetbrains.php.tools.quality.QualityToolAnnotatorInfo;
12 | import com.jetbrains.php.tools.quality.QualityToolMessage;
13 | import com.jetbrains.php.tools.quality.QualityToolXmlMessageProcessor;
14 | import org.jetbrains.annotations.NonNls;
15 | import org.jetbrains.annotations.NotNull;
16 | import org.jetbrains.annotations.Nullable;
17 | import org.xml.sax.Attributes;
18 | import org.xml.sax.InputSource;
19 | import org.xml.sax.SAXException;
20 |
21 | import java.io.IOException;
22 | import java.util.ArrayList;
23 | import java.util.HashSet;
24 | import java.util.List;
25 | import java.util.Set;
26 |
27 | import static com.jetbrains.php.tools.quality.QualityToolMessage.Severity.ERROR;
28 | import static com.jetbrains.php.tools.quality.QualityToolMessage.Severity.WARNING;
29 | import static com.jetbrains.php.tools.quality.phpstan.PhpStanGlobalInspection.PHPSTAN_ANNOTATOR_INFO;
30 |
31 | public class PhpStanMessageProcessor extends QualityToolXmlMessageProcessor {
32 | private static final String ERROR_TAG = "error";
33 | private static final String FILE_TAG = "file";
34 | private static final @NonNls String WARNING_MESSAGE_START = " lineMessages = new HashSet<>();
43 | private final HighlightDisplayLevel myWarningsHighlightLevel;
44 | final String myFilePath;
45 | final PsiFile myPsiFile;
46 | final Project myProject;
47 |
48 | protected PhpStanMessageProcessor(QualityToolAnnotatorInfo> info) {
49 | super(info);
50 | myWarningsHighlightLevel = HighlightDisplayLevel.WARNING; // TODO: fix
51 | myFilePath = info.getTempFilePath();
52 | myPsiFile = info.getPsiFile();
53 | myProject = info.getProject();
54 | }
55 |
56 | @Override
57 | protected boolean show(@NotNull String message) {
58 | return !message.contains("The Xdebug PHP extension is active, but \"--xdebug\" is not used");
59 | }
60 |
61 | @Override
62 | protected void processMessage(InputSource source) throws SAXException, IOException {
63 | PhpStanXmlMessageHandler messageHandler = (PhpStanXmlMessageHandler)getXmlMessageHandler(myFilePath);
64 | mySAXParser.parse(source, messageHandler);
65 | if (messageHandler.isStatusValid()) {
66 | if (myPsiFile != null) {
67 | List list = messageHandler.getProblemList();
68 | if (list == null) return;
69 | for (ProblemDescription problem : list) {
70 | if (myProject.isDisposed()) return;
71 | Document document = ReadAction.compute(() -> PsiDocumentManager.getInstance(myPsiFile.getProject()).getDocument(myPsiFile));
72 | QualityToolMessage qualityToolMessage;
73 | if (document != null && problem.getLineNumber() - 1 > 0 && problem.getLineNumber() - 1 < document.getLineCount()) {
74 | qualityToolMessage = new QualityToolMessage(this, TextRange
75 | .create(document.getLineStartOffset(problem.getLineNumber() - 1) + problem.getColumn(),
76 | document.getLineEndOffset(problem.getLineNumber() - 1)), problem.getSeverity(), problem.getMessage());
77 | }
78 | else {
79 | qualityToolMessage = new QualityToolMessage(this, problem.getLineNumber(), problem.getSeverity(), problem.getMessage());
80 | }
81 | if (lineMessages.add(problem)) {
82 | addMessage(qualityToolMessage);
83 | }
84 | }
85 | } else {
86 | final List data = myProject.getUserData(PHPSTAN_ANNOTATOR_INFO);
87 | if (data != null) {
88 | data.addAll(messageHandler.getProblemList());
89 | myProject.putUserData(PHPSTAN_ANNOTATOR_INFO, data);
90 | }
91 | else {
92 | myProject.putUserData(PHPSTAN_ANNOTATOR_INFO, messageHandler.getProblemList());
93 | }
94 | }
95 | }
96 | }
97 |
98 | @Override
99 | protected XMLMessageHandler getXmlMessageHandler() {
100 | return null;
101 | }
102 |
103 | protected XMLMessageHandler getXmlMessageHandler(@Nullable String filePath) {
104 | return new PhpStanXmlMessageHandler(filePath);
105 | }
106 |
107 | @Override
108 | public int getMessageStart(@NotNull String line) {
109 | return line.indexOf(WARNING_MESSAGE_START);
110 | }
111 |
112 | @Override
113 | public int getMessageEnd(@NotNull String line) {
114 | return line.indexOf(WARNING_MESSAGE_END);
115 | }
116 |
117 | @Override
118 | protected @NonNls @Nullable String getMessagePrefix() {
119 | return "phpstan";
120 | }
121 |
122 | @Override
123 | protected @Nullable HighlightDisplayLevel severityToDisplayLevel(@NotNull QualityToolMessage.Severity severity) {
124 | return WARNING.equals(severity) ? myWarningsHighlightLevel : null;
125 | }
126 |
127 | @Override
128 | protected PhpStanQualityToolType getQualityToolType() {
129 | return PhpStanQualityToolType.INSTANCE;
130 | }
131 |
132 | static final class PhpStanXmlMessageHandler extends XMLMessageHandler {
133 |
134 | private final String myFilePath;
135 | private String myFileAttr;
136 |
137 | private PhpStanXmlMessageHandler(@Nullable String filePath) {
138 | myFilePath = filePath;
139 | }
140 |
141 | private List myProblemList;
142 |
143 | private List getProblemList() {
144 | return myProblemList;
145 | }
146 |
147 | @Override
148 | protected void parseTag(@NotNull String tagName, @NotNull Attributes attributes) {
149 | if (FILE_TAG.equals(tagName)) {
150 | myFileAttr = PathUtil.toSystemIndependentName(attributes.getValue(FILE_NAME_ATTR));
151 | myProblemList = myFilePath == null || myFilePath.endsWith(myFileAttr == null ? "": myFileAttr) ? new ArrayList<>() : null;
152 | }
153 | else if (ERROR_TAG.equals(tagName) || WARNING_TAG.equals(tagName)) {
154 | if (myProblemList != null) {
155 | mySeverity = attributes.getValue(SEVERITY_ATTR).equals(ERROR_TAG) ? ERROR : WARNING;
156 | myLineNumber = parseLineNumber(attributes.getValue(LINE_NUMBER_ATTR));
157 | int column = parseLineNumber(attributes.getValue(COLUMN_NUMBER_ATTR));
158 | myProblemList
159 | .add(new ProblemDescription(mySeverity, myLineNumber, Math.max(0, column - 1), attributes.getValue(MESSAGE_ATTR), myFileAttr));
160 | }
161 | }
162 | }
163 | }
164 | }
--------------------------------------------------------------------------------
/testData/parser/DocMethodsTags.txt:
--------------------------------------------------------------------------------
1 |
37 | PsiElement())(')')
38 | PsiWhiteSpace(' ')
39 | PhpDocMethodImpl: borp
40 | PsiElement(identifier)('borp')
41 | PsiElement(()('(')
42 | Parameter list
43 | ParameterImpl: int1
44 | PhpPsiElementImpl
45 | ClassReferenceImpl: int
46 | PsiElement(identifier)('int')
47 | PsiWhiteSpace(' ')
48 | PsiElement(variable)('$int1')
49 | PsiElement(comma)(',')
50 | PsiWhiteSpace(' ')
51 | ParameterImpl: int2
52 | PhpPsiElementImpl
53 | ClassReferenceImpl: int
54 | PsiElement(identifier)('int')
55 | PsiElement([)('[')
56 | PsiElement(])(']')
57 | PsiWhiteSpace(' ')
58 | PsiElement(variable)('$int2')
59 | PsiElement())(')')
60 | PsiWhiteSpace(' ')
61 | PhpPsiElementImpl
62 | PsiElement(identifier)('multiply')
63 | PsiWhiteSpace(' ')
64 | PsiElement(identifier)('two')
65 | PsiWhiteSpace(' ')
66 | PsiElement(identifier)('integers')
67 | PsiWhiteSpace('\n ')
68 | PsiElement(DOC_LEADING_ASTERISK)('*')
69 | PsiWhiteSpace(' ')
70 | PhpDocMethodTagImpl: method
71 | PsiElement(error silence)('@')
72 | PsiElement(identifier)('method')
73 | PsiWhiteSpace(' ')
74 | PhpPsiElementImpl
75 | ClassReferenceImpl: int
76 | PsiElement(identifier)('int')
77 | PsiElement(bit or)('|')
78 | PhpPsiElementImpl
79 | ClassReferenceImpl: array
80 | PsiElement(array)('array')
81 | PsiWhiteSpace(' ')
82 | PsiElement(identifier)('borp')
83 | PsiElement(()('(')
84 | Parameter list
85 |
86 | PsiElement())(')')
87 | PsiWhiteSpace(' ')
88 | PhpDocMethodImpl: borp
89 | PsiElement(identifier)('borp')
90 | PsiElement(()('(')
91 | Parameter list
92 | ParameterImpl: int1
93 | PhpPsiElementImpl
94 | ClassReferenceImpl: int
95 | PsiElement(identifier)('int')
96 | PsiWhiteSpace(' ')
97 | PsiElement(variable)('$int1')
98 | PsiElement(comma)(',')
99 | PsiWhiteSpace(' ')
100 | ParameterImpl: int2
101 | PhpPsiElementImpl
102 | ClassReferenceImpl: int
103 | PsiElement(identifier)('int')
104 | PsiElement([)('[')
105 | PsiElement(])(']')
106 | PsiWhiteSpace(' ')
107 | PsiElement(variable)('$int2')
108 | PsiElement())(')')
109 | PsiWhiteSpace(' ')
110 | PhpPsiElementImpl
111 | PsiElement(identifier)('multiply')
112 | PsiWhiteSpace(' ')
113 | PsiElement(identifier)('two')
114 | PsiWhiteSpace(' ')
115 | PsiElement(identifier)('integers')
116 | PsiWhiteSpace('\n ')
117 | PsiElement(DOC_LEADING_ASTERISK)('*')
118 | PsiWhiteSpace(' ')
119 | PhpDocTagImpl: @phpstan-methodWrong
120 | PsiElement(DOC_TAG_NAME)('@phpstan-methodWrong')
121 | PsiWhiteSpace(' ')
122 | PhpPsiElementImpl
123 | PsiElement(DOC_IDENTIFIER)('int')
124 | PsiElement(DOC_PIPE)('|')
125 | PsiElement(DOC_IDENTIFIER)('array')
126 | PsiWhiteSpace(' ')
127 | PsiElement(DOC_IDENTIFIER)('borp')
128 | PsiElement(DOC_LPAREN)('(')
129 | PsiElement(DOC_RPAREN)(')')
130 | PsiWhiteSpace(' ')
131 | PsiElement(DOC_IDENTIFIER)('borp')
132 | PsiElement(DOC_LPAREN)('(')
133 | PsiElement(DOC_IDENTIFIER)('int')
134 | PsiWhiteSpace(' ')
135 | PsiElement(DOC_VARIABLE)('$int1')
136 | PsiElement(DOC_COMMA)(',')
137 | PsiWhiteSpace(' ')
138 | PsiElement(DOC_IDENTIFIER)('int')
139 | PsiElement(DOC_LBRACKET)('[')
140 | PsiElement(DOC_RBRACKET)(']')
141 | PsiWhiteSpace(' ')
142 | PsiElement(DOC_VARIABLE)('$int2')
143 | PsiElement(DOC_RPAREN)(')')
144 | PsiWhiteSpace(' ')
145 | PsiElement(DOC_IDENTIFIER)('multiply')
146 | PsiWhiteSpace(' ')
147 | PsiElement(DOC_IDENTIFIER)('two')
148 | PsiWhiteSpace(' ')
149 | PsiElement(DOC_IDENTIFIER)('integers')
150 | PsiWhiteSpace('\n ')
151 | PsiElement(DOC_LEADING_ASTERISK)('*')
152 | PsiWhiteSpace(' ')
153 | PhpDocTagImpl: @wrong-method
154 | PsiElement(DOC_TAG_NAME)('@wrong-method')
155 | PsiWhiteSpace(' ')
156 | PhpPsiElementImpl
157 | PsiElement(DOC_IDENTIFIER)('int')
158 | PsiElement(DOC_LBRACKET)('[')
159 | PsiElement(DOC_RBRACKET)(']')
160 | PsiWhiteSpace(' ')
161 | PsiElement(DOC_IDENTIFIER)('borp')
162 | PsiElement(DOC_LPAREN)('(')
163 | PsiElement(DOC_IDENTIFIER)('int')
164 | PsiElement(DOC_PIPE)('|')
165 | PsiElement(DOC_IDENTIFIER)('string')
166 | PsiWhiteSpace(' ')
167 | PsiElement(DOC_VARIABLE)('$int1')
168 | PsiElement(DOC_COMMA)(',')
169 | PsiWhiteSpace(' ')
170 | PsiElement(DOC_IDENTIFIER)('int')
171 | PsiWhiteSpace(' ')
172 | PsiElement(DOC_VARIABLE)('$int2')
173 | PsiElement(DOC_RPAREN)(')')
174 | PsiWhiteSpace(' ')
175 | PsiElement(DOC_IDENTIFIER)('multiply')
176 | PsiWhiteSpace(' ')
177 | PsiElement(DOC_IDENTIFIER)('two')
178 | PsiWhiteSpace(' ')
179 | PsiElement(DOC_IDENTIFIER)('integers')
180 | PsiWhiteSpace('\n ')
181 | PsiElement(DOC_COMMENT_END)('*/')
182 | PsiWhiteSpace('\n')
183 | PhpClass: Cart
184 | PsiElement(class)('class')
185 | PsiWhiteSpace(' ')
186 | PsiElement(identifier)('Cart')
187 | PsiWhiteSpace(' ')
188 | Extends list
189 |
190 | Implements list
191 |
192 | PsiElement({)('{')
193 | PsiWhiteSpace('\n')
194 | PsiElement(})('}')
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanComposerConfig.java:
--------------------------------------------------------------------------------
1 | package com.jetbrains.php.tools.quality.phpstan;
2 |
3 | import com.google.gson.JsonElement;
4 | import com.google.gson.JsonObject;
5 | import com.google.gson.JsonParseException;
6 | import com.intellij.openapi.application.ReadAction;
7 | import com.intellij.openapi.project.Project;
8 | import com.intellij.openapi.util.Key;
9 | import com.intellij.openapi.util.NlsSafe;
10 | import com.intellij.openapi.util.Ref;
11 | import com.intellij.openapi.util.SystemInfo;
12 | import com.intellij.openapi.util.text.StringUtil;
13 | import com.intellij.openapi.vfs.LocalFileSystem;
14 | import com.intellij.openapi.vfs.VirtualFile;
15 | import com.intellij.profile.codeInspection.InspectionProfileManager;
16 | import com.intellij.psi.PsiDirectory;
17 | import com.intellij.psi.PsiManager;
18 | import com.intellij.util.Consumer;
19 | import com.jetbrains.php.composer.ComposerDataService;
20 | import com.jetbrains.php.composer.actions.log.ComposerLogMessageBuilder;
21 | import com.jetbrains.php.tools.quality.QualityToolConfigurationManager;
22 | import com.jetbrains.php.tools.quality.QualityToolsComposerConfig;
23 | import org.jetbrains.annotations.NonNls;
24 | import org.jetbrains.annotations.NotNull;
25 | import org.jetbrains.annotations.Nullable;
26 |
27 | import java.io.IOException;
28 | import java.util.List;
29 |
30 | import static com.intellij.openapi.util.text.StringUtil.*;
31 | import static com.jetbrains.php.composer.ComposerConfigUtils.parseJson;
32 | import static com.jetbrains.php.tools.quality.phpstan.PhpStanOpenSettingsProvider.PHP_STAN_OPEN_SETTINGS_PROVIDER;
33 |
34 | public class PhpStanComposerConfig extends QualityToolsComposerConfig {
35 | private static final @NonNls String PACKAGE = "phpstan/phpstan";
36 | private static final @NonNls String RELATIVE_PATH = "bin/phpstan" + (SystemInfo.isWindows ? ".bat" : "");
37 | private static final @NonNls String PHPSTAN_NEON = "phpstan.neon";
38 |
39 | public PhpStanComposerConfig() {
40 | super(PACKAGE, RELATIVE_PATH);
41 | }
42 |
43 | @Override
44 | public String getQualityInspectionShortName() {
45 | return PhpStanQualityToolType.INSTANCE.getInspectionId();
46 | }
47 |
48 | @Override
49 | protected boolean applyRulesetFromComposer(@NotNull Project project, PhpStanConfiguration configuration) {
50 | final String configPath = ComposerDataService.getInstance(project).getConfigPath();
51 | PhpStanOptionsConfiguration projectConfiguration = PhpStanOptionsConfiguration.getInstance(project);
52 |
53 | final VirtualFile config = LocalFileSystem.getInstance().refreshAndFindFileByPath(configPath);
54 | if (config == null) return false;
55 |
56 | final String ruleset = getRuleset(config);
57 | if (ruleset == null) return false;
58 | final VirtualFile customRulesetFile = detectCustomRulesetFile(config.getParent(), ruleset);
59 | final boolean customRulesetChanged = customRulesetFile != null &&
60 | modifyRulesetPhpStanInspectionSetting(project, tool -> applyRuleset(projectConfiguration,
61 | customRulesetFile.getPath()));
62 |
63 | final String memoryLimit = getMemoryLimit(config);
64 | final boolean memoryLimitChanged =
65 | memoryLimit != null && modifyRulesetPhpStanInspectionSetting(project, tool -> applyMemoryLimit(projectConfiguration, memoryLimit));
66 |
67 | return customRulesetChanged || memoryLimitChanged;
68 | }
69 |
70 | @Override
71 | protected void applyInspectionSettingsFromComposer(Project project, PhpStanConfiguration configuration) {
72 | final String configPath = ComposerDataService.getInstance(project).getConfigPath();
73 | final VirtualFile config = LocalFileSystem.getInstance().refreshAndFindFileByPath(configPath);
74 | if (config == null) return;
75 |
76 | final String memoryLimit = getMemoryLimit(config);
77 | if (memoryLimit != null) {
78 | modifyRulesetPhpStanInspectionSetting(project,
79 | tool -> applyMemoryLimit(PhpStanOptionsConfiguration.getInstance(project), memoryLimit));
80 | }
81 | }
82 |
83 | private @Nullable String getMemoryLimit(VirtualFile config) {
84 | JsonElement element;
85 | try {
86 | element = parseJson(config);
87 | }
88 | catch (IOException | JsonParseException e) {
89 | return null;
90 | }
91 |
92 | if (element instanceof JsonObject) {
93 | final JsonElement scriptElement = ((JsonObject)element).get("scripts");
94 | if (scriptElement != null) {
95 | final Ref result = new Ref<>();
96 | parse(scriptElement, result, (el, res) -> parseLimit(el, result));
97 | return result.isNull() ? null : result.get();
98 | }
99 | }
100 | return null;
101 | }
102 |
103 | private static void parseLimit(JsonElement el, Ref result) {
104 | final String string = el.getAsString();
105 | if (string != null && string.contains("phpstan")) {
106 | final List split = split(string, " ");
107 | for (String arg: split) {
108 | final String prefix = "--memory-limit=";
109 | if (startsWith(arg, prefix)) {
110 | result.set(trimStart(arg, prefix));
111 | return;
112 | }
113 | final int index = split.indexOf(arg);
114 | if (StringUtil.equals(arg, "--memory-limit") && index < split.size() - 1) {
115 | result.set(split.get(index + 1));
116 | return;
117 | }
118 | }
119 | }
120 | }
121 |
122 | @Override
123 | protected boolean applyRulesetFromRoot(@NotNull Project project) {
124 | VirtualFile customRulesetFile = detectCustomRulesetFile(project.getBaseDir(), PHPSTAN_NEON);
125 | if(customRulesetFile == null){
126 | customRulesetFile = detectCustomRulesetFile(project.getBaseDir(), PHPSTAN_NEON + ".dist");
127 | }
128 |
129 | if (customRulesetFile != null) {
130 | final String path = customRulesetFile.getPath();
131 | return modifyRulesetPhpStanInspectionSetting(project, tool -> applyRuleset(PhpStanOptionsConfiguration.getInstance(project), path));
132 | }
133 | return false;
134 | }
135 |
136 | @Override
137 | protected void checkComposerScriptsLeaves(JsonElement element, Ref result) {
138 | final String string = element.getAsString();
139 | if (string != null && string.contains("phpstan")) {
140 | final List split = split(string, " ");
141 | for (String arg: split) {
142 | final String prefix = "--configuration=";
143 | if (startsWith(arg, prefix)) {
144 | result.set(trimStart(arg, prefix));
145 | return;
146 | }
147 | final int index = split.indexOf(arg);
148 | if (StringUtil.equals(arg, "-c") && index < split.size() - 1) {
149 | result.set(split.get(index + 1));
150 | return;
151 | }
152 | }
153 | }
154 | }
155 |
156 | @Override
157 | public @Nullable ComposerLogMessageBuilder.Settings getSettings() {
158 | return PHP_STAN_OPEN_SETTINGS_PROVIDER;
159 | }
160 |
161 | private static void applyRuleset(PhpStanOptionsConfiguration configuration, @NlsSafe String customRuleset) {
162 | configuration.setConfig(customRuleset);
163 | }
164 |
165 | private static void applyMemoryLimit(PhpStanOptionsConfiguration configuration, @NlsSafe String memoryLimit) {
166 | configuration.setMemoryLimit(memoryLimit);
167 | }
168 |
169 | protected boolean modifyRulesetPhpStanInspectionSetting(@NotNull Project project, @NotNull Consumer consumer) {
170 | VirtualFile projectDir = project.getBaseDir();
171 | if (projectDir == null) return false;
172 |
173 | PsiDirectory file = ReadAction.compute(() -> PsiManager.getInstance(project).findDirectory(projectDir));
174 | if (file != null) {
175 | Key key = Key.create(PhpStanQualityToolType.INSTANCE.getInspectionId());
176 | InspectionProfileManager.getInstance(project).getCurrentProfile().modifyToolSettings(key, file, consumer);
177 | return true;
178 | }
179 | return false;
180 | }
181 |
182 | @Override
183 | public @NotNull QualityToolConfigurationManager getConfigurationManager(@NotNull Project project) {
184 | return PhpStanConfigurationManager.getInstance(project);
185 | }
186 | }
--------------------------------------------------------------------------------
/src/com/jetbrains/php/tools/quality/phpstan/PhpStanOptionsPanel.form:
--------------------------------------------------------------------------------
1 |
2 |
140 |
--------------------------------------------------------------------------------
/testData/parser/Assert.txt:
--------------------------------------------------------------------------------
1 |
23 | * @phpstan-assert-if-false int $this->test()
24 | * @phpstan-assert-if-false int $b->test()
25 | * @phpstan-assert-if-true null $this->b
26 | * @template NewT
27 | * @phpstan-this-out self
28 | * @phpstan-self-out self
29 | */
30 | public function test(B|A $b): void {
31 |
32 | }
33 | }
34 | ---
35 | PHP file
36 | PsiElement(Non Lazy Group statement)
37 | PsiElement(php opening tag)('
56 | PsiWhiteSpace('\n ')
57 | PsiElement(DOC_LEADING_ASTERISK)('*')
58 | PsiWhiteSpace(' ')
59 | PhpDocTagImpl: @phpstan-assert-if-false
60 | PsiElement(DOC_TAG_NAME)('@phpstan-assert-if-false')
61 | PsiWhiteSpace(' ')
62 | PhpDocTypeImpl: B
63 | PsiElement(DOC_IDENTIFIER)('B')
64 | PsiWhiteSpace(' ')
65 | PhpDocVarImpl: a
66 | PsiElement(DOC_VARIABLE)('$a')
67 | PhpPsiElementImpl
68 |
69 | PsiWhiteSpace('\n ')
70 | PsiElement(DOC_LEADING_ASTERISK)('*')
71 | PsiWhiteSpace(' ')
72 | PhpDocTagImpl: @phpstan-assert-if-true
73 | PsiElement(DOC_TAG_NAME)('@phpstan-assert-if-true')
74 | PsiWhiteSpace(' ')
75 | PhpDocTypeImpl: A
76 | PsiElement(DOC_IDENTIFIER)('A')
77 | PsiWhiteSpace(' ')
78 | PhpDocVarImpl: b
79 | PsiElement(DOC_VARIABLE)('$b')
80 | PhpPsiElementImpl
81 |
82 | PsiWhiteSpace('\n ')
83 | PsiElement(DOC_COMMENT_END)('*/')
84 | PsiWhiteSpace('\n')
85 | FunctionImpl: a
86 | PsiElement(function)('function')
87 | PsiWhiteSpace(' ')
88 | PsiElement(identifier)('a')
89 | PsiElement(()('(')
90 | Parameter list
91 | ParameterImpl: arr
92 | Parameter type
93 | ClassReferenceImpl: array
94 | PsiElement(array)('array')
95 | PsiWhiteSpace(' ')
96 | PsiElement(variable)('$arr')
97 | PsiElement(comma)(',')
98 | PsiWhiteSpace(' ')
99 | ParameterImpl: arg
100 | Parameter type
101 | ClassReferenceImpl: array
102 | PsiElement(array)('array')
103 | PsiWhiteSpace(' ')
104 | PsiElement(variable)('$arg')
105 | PsiElement())(')')
106 | PsiWhiteSpace(' ')
107 | PsiElement(colon)(':')
108 | PsiWhiteSpace(' ')
109 | Return type
110 | ClassReferenceImpl: void
111 | PsiElement(identifier)('void')
112 | PsiWhiteSpace(' ')
113 | PsiElement(Group statement)
114 | PsiElement({)('{')
115 | PsiWhiteSpace('\n\n')
116 | PsiElement(})('}')
117 | PsiWhiteSpace('\n\n')
118 | PhpClass: B
119 | PsiElement(class)('class')
120 | PsiWhiteSpace(' ')
121 | PsiElement(identifier)('B')
122 | PsiWhiteSpace(' ')
123 | Extends list
124 |
125 | Implements list
126 |
127 | PsiElement({)('{')
128 | PsiWhiteSpace('\n ')
129 | MethodImpl: test
130 | PhpModifierListImpl: %EMPTY%
131 |
132 | PsiElement(function)('function')
133 | PsiWhiteSpace(' ')
134 | PsiElement(identifier)('test')
135 | PsiElement(()('(')
136 | Parameter list
137 |
138 | PsiElement())(')')
139 | PsiWhiteSpace(' ')
140 | PsiElement(Group statement)
141 | PsiElement({)('{')
142 | PsiWhiteSpace('\n\n ')
143 | PsiElement(})('}')
144 | PsiWhiteSpace('\n')
145 | PsiElement(})('}')
146 | PsiWhiteSpace('\n\n')
147 | PhpClass: a
148 | PsiElement(class)('class')
149 | PsiWhiteSpace(' ')
150 | PsiElement(identifier)('a')
151 | PsiWhiteSpace(' ')
152 | Extends list
153 |
154 | Implements list
155 |
156 | PsiElement({)('{')
157 | PsiWhiteSpace('\n ')
158 | PhpPsiElementImpl
159 | PhpModifierListImpl: public
160 | PsiElement(public keyword)('public')
161 | PsiWhiteSpace(' ')
162 | Field type
163 | ClassReferenceImpl: int
164 | PsiElement(identifier)('int')
165 | PsiWhiteSpace(' ')
166 | FieldImpl: b
167 | PsiElement(variable)('$b')
168 | PsiWhiteSpace(' ')
169 | PsiElement(assign)('=')
170 | PsiWhiteSpace(' ')
171 | PhpExpressionImpl: 0
172 | PsiElement(integer)('0')
173 | PsiElement(semicolon)(';')
174 | PsiWhiteSpace('\n\n ')
175 | PhpDocCommentImpl
176 | PsiElement(DOC_COMMENT_START)('/**')
177 | PsiWhiteSpace('\n ')
178 | PsiElement(DOC_LEADING_ASTERISK)('*')
179 | PsiWhiteSpace(' ')
180 | PhpDocTagImpl: @phpstan-if-this-is
181 | PsiElement(DOC_TAG_NAME)('@phpstan-if-this-is')
182 | PsiWhiteSpace(' ')
183 | PhpDocTypeImpl: a
184 | PsiElement(DOC_IDENTIFIER)('a')
185 | PhpPsiElementImpl
186 | PsiElement(DOC_LAB)('<')
187 | PhpDocTypeImpl: int
188 | PsiElement(DOC_IDENTIFIER)('int')
189 | PsiElement(DOC_RAB)('>')
190 | PhpPsiElementImpl
191 |
192 | PsiWhiteSpace('\n ')
193 | PsiElement(DOC_LEADING_ASTERISK)('*')
194 | PsiWhiteSpace(' ')
195 | PhpDocTagImpl: @phpstan-assert-if-false
196 | PsiElement(DOC_TAG_NAME)('@phpstan-assert-if-false')
197 | PsiWhiteSpace(' ')
198 | PhpDocTypeImpl: int
199 | PsiElement(DOC_IDENTIFIER)('int')
200 | PsiWhiteSpace(' ')
201 | PhpDocRefImpl: test
202 | PhpDocVarImpl: this
203 | PsiElement(DOC_VARIABLE)('$this')
204 | PsiElement(DOC_ARROW)('->')
205 | PsiElement(DOC_IDENTIFIER)('test')
206 | PsiElement(DOC_LPAREN)('(')
207 | PsiElement(DOC_RPAREN)(')')
208 | PhpPsiElementImpl
209 |
210 | PsiWhiteSpace('\n ')
211 | PsiElement(DOC_LEADING_ASTERISK)('*')
212 | PsiWhiteSpace(' ')
213 | PhpDocTagImpl: @phpstan-assert-if-false
214 | PsiElement(DOC_TAG_NAME)('@phpstan-assert-if-false')
215 | PsiWhiteSpace(' ')
216 | PhpDocTypeImpl: int
217 | PsiElement(DOC_IDENTIFIER)('int')
218 | PsiWhiteSpace(' ')
219 | PhpDocRefImpl: test
220 | PhpDocVarImpl: b
221 | PsiElement(DOC_VARIABLE)('$b')
222 | PsiElement(DOC_ARROW)('->')
223 | PsiElement(DOC_IDENTIFIER)('test')
224 | PsiElement(DOC_LPAREN)('(')
225 | PsiElement(DOC_RPAREN)(')')
226 | PhpPsiElementImpl
227 |
228 | PsiWhiteSpace('\n ')
229 | PsiElement(DOC_LEADING_ASTERISK)('*')
230 | PsiWhiteSpace(' ')
231 | PhpDocTagImpl: @phpstan-assert-if-true
232 | PsiElement(DOC_TAG_NAME)('@phpstan-assert-if-true')
233 | PsiWhiteSpace(' ')
234 | PhpDocTypeImpl: null
235 | PsiElement(DOC_IDENTIFIER)('null')
236 | PsiWhiteSpace(' ')
237 | PhpDocRefImpl: b
238 | PhpDocVarImpl: this
239 | PsiElement(DOC_VARIABLE)('$this')
240 | PsiElement(DOC_ARROW)('->')
241 | PsiElement(DOC_IDENTIFIER)('b')
242 | PhpPsiElementImpl
243 |
244 | PsiWhiteSpace('\n ')
245 | PsiElement(DOC_LEADING_ASTERISK)('*')
246 | PsiWhiteSpace(' ')
247 | PhpDocTemplateTagImpl: @template
248 | PsiElement(DOC_TAG_NAME)('@template')
249 | PsiWhiteSpace(' ')
250 | PhpDocTemplateParameterImpl: NewT
251 | PsiElement(DOC_IDENTIFIER)('NewT')
252 | PhpPsiElementImpl
253 |
254 | PsiWhiteSpace('\n ')
255 | PsiElement(DOC_LEADING_ASTERISK)('*')
256 | PsiWhiteSpace(' ')
257 | PhpDocTagImpl: @phpstan-this-out
258 | PsiElement(DOC_TAG_NAME)('@phpstan-this-out')
259 | PsiWhiteSpace(' ')
260 | PhpDocTypeImpl: self
261 | PsiElement(DOC_IDENTIFIER)('self')
262 | PhpPsiElementImpl
263 | PsiElement(DOC_LAB)('<')
264 | PhpDocTypeImpl: NewT
265 | PsiElement(DOC_IDENTIFIER)('NewT')
266 | PsiElement(DOC_RAB)('>')
267 | PhpPsiElementImpl
268 |
269 | PsiWhiteSpace('\n ')
270 | PsiElement(DOC_LEADING_ASTERISK)('*')
271 | PsiWhiteSpace(' ')
272 | PhpDocTagImpl: @phpstan-self-out
273 | PsiElement(DOC_TAG_NAME)('@phpstan-self-out')
274 | PsiWhiteSpace(' ')
275 | PhpDocTypeImpl: self
276 | PsiElement(DOC_IDENTIFIER)('self')
277 | PhpPsiElementImpl
278 | PsiElement(DOC_LAB)('<')
279 | PhpDocTypeImpl: NewT
280 | PsiElement(DOC_IDENTIFIER)('NewT')
281 | PsiElement(DOC_RAB)('>')
282 | PhpPsiElementImpl
283 |
284 | PsiWhiteSpace('\n ')
285 | PsiElement(DOC_COMMENT_END)('*/')
286 | PsiWhiteSpace('\n ')
287 | MethodImpl: test
288 | PhpModifierListImpl: public
289 | PsiElement(public keyword)('public')
290 | PsiWhiteSpace(' ')
291 | PsiElement(function)('function')
292 | PsiWhiteSpace(' ')
293 | PsiElement(identifier)('test')
294 | PsiElement(()('(')
295 | Parameter list
296 | ParameterImpl: b
297 | Parameter type
298 | ClassReferenceImpl: B
299 | PsiElement(identifier)('B')
300 | PsiElement(bit or)('|')
301 | ClassReferenceImpl: A
302 | PsiElement(identifier)('A')
303 | PsiWhiteSpace(' ')
304 | PsiElement(variable)('$b')
305 | PsiElement())(')')
306 | PsiElement(colon)(':')
307 | PsiWhiteSpace(' ')
308 | Return type
309 | ClassReferenceImpl: void
310 | PsiElement(identifier)('void')
311 | PsiWhiteSpace(' ')
312 | PsiElement(Group statement)
313 | PsiElement({)('{')
314 | PsiWhiteSpace('\n\n ')
315 | PsiElement(})('}')
316 | PsiWhiteSpace('\n')
317 | PsiElement(})('}')
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2010-2020 JetBrains s.r.o.
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------