Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,23 @@ public static ElementPattern<PsiElement> getSingleLineScalarKey(String... keyNam
);
}

public static ElementPattern<PsiElement> getNamedArgumentPattern() {
return PlatformPatterns
.psiElement(YAMLTokenTypes.SCALAR_KEY).withText(PlatformPatterns.string().startsWith("$"))
.withParent(
PlatformPatterns.psiElement(YAMLKeyValue.class)
.withParent(PlatformPatterns.psiElement(YAMLMapping.class)
.withParent(PlatformPatterns.psiElement(YAMLKeyValue.class).with(new PatternCondition<>("YAMLKeyValue: with key 'arguments'") {
@Override
public boolean accepts(@NotNull YAMLKeyValue yamlKeyValue, ProcessingContext context) {
return "arguments".equals(yamlKeyValue.getKeyText());
}
}))
)
)
.withLanguage(YAMLLanguage.INSTANCE);
}

public static ElementPattern<PsiElement> getSingleLineText() {
return
PlatformPatterns
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,44 @@ public static Parameter getYamlNamedArgument(@NotNull PsiElement psiElement, @No
return null;
}

/**
* arguments: ['$foobar': '@foo']
*/
public static boolean hasMissingYamlNamedArgumentForInspection(@NotNull PsiElement psiElement, @NotNull ContainerCollectionResolver.LazyServiceCollector lazyServiceCollector) {
PsiElement context = psiElement.getContext();
if(context instanceof YAMLKeyValue) {
// arguments: ['$foobar': '@foo']

String parameterName = ((YAMLKeyValue) context).getKeyText();
if(parameterName.startsWith("$") && parameterName.length() > 1) {
PsiElement yamlMapping = context.getParent();
if(yamlMapping instanceof YAMLMapping) {
PsiElement yamlKeyValue = yamlMapping.getParent();
if(yamlKeyValue instanceof YAMLKeyValue) {
String keyText = ((YAMLKeyValue) yamlKeyValue).getKeyText();
if(keyText.equals("arguments")) {
YAMLMapping parentMapping = ((YAMLKeyValue) yamlKeyValue).getParentMapping();
if(parentMapping != null) {
String serviceId = getServiceClassFromServiceMapping(parentMapping);
if(StringUtils.isNotBlank(serviceId)) {
PhpClass serviceClass = ServiceUtil.getResolvedClassDefinition(psiElement.getProject(), serviceId, lazyServiceCollector);
// class not found don't need a hint
if (serviceClass == null) {
return false;
}

return PhpElementsUtil.getConstructorParameterArgumentByName(serviceClass, StringUtils.stripStart(parameterName, "$")) == null;
}
}
}
}
}
}
}

return false;
}

/**
* services:
* _defaults:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package fr.adrienbrault.idea.symfony2plugin.dic.inspection;

import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
import fr.adrienbrault.idea.symfony2plugin.config.yaml.YamlElementPatternHelper;
import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil;
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.yaml.psi.YAMLKeyValue;
import org.jetbrains.yaml.psi.YAMLMapping;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class ServiceNamedArgumentExistsInspection extends LocalInspectionTool {
public static final String INSPECTION_MESSAGE = "Symfony: named argument does not exists";

@NotNull
public PsiElementVisitor buildVisitor(final @NotNull ProblemsHolder holder, boolean isOnTheFly) {
if (!Symfony2ProjectComponent.isEnabled(holder.getProject())) {
return super.buildVisitor(holder, isOnTheFly);
}

return new PsiElementVisitor() {
@Override
public void visitElement(@NotNull PsiElement element) {
if (YamlElementPatternHelper.getNamedArgumentPattern().accepts(element)) {
if (isSupportedDefinition(element) && ServiceContainerUtil.hasMissingYamlNamedArgumentForInspection(element, new ContainerCollectionResolver.LazyServiceCollector(element.getProject()))) {
holder.registerProblem(element, INSPECTION_MESSAGE, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
}
}

super.visitElement(element);
}
};
}

private boolean isSupportedDefinition(@NotNull PsiElement element) {
PsiElement context = element.getContext();

if (context instanceof YAMLKeyValue) {
// arguments: ['$foobar': '@foo']
PsiElement yamlMapping = context.getParent();
if (yamlMapping instanceof YAMLMapping) {
PsiElement yamlKeyValue = yamlMapping.getParent();
if (yamlKeyValue instanceof YAMLKeyValue) {
YAMLMapping parentMapping = ((YAMLKeyValue) yamlKeyValue).getParentMapping();
if (parentMapping != null) {
return parentMapping.getKeyValueByKey("factory") == null;
}
}
}
}

return true;
}
}
6 changes: 6 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,12 @@
level="WARNING"
implementationClass="fr.adrienbrault.idea.symfony2plugin.routing.RouteControllerDeprecatedInspection"/>

<localInspection groupPath="Symfony" shortName="ServiceNamedArgumentExistsInspection" displayName="Symfony: Argument does not exists"
groupName="Service"
enabledByDefault="true"
level="WARNING"
implementationClass="fr.adrienbrault.idea.symfony2plugin.dic.inspection.ServiceNamedArgumentExistsInspection"/>

<intentionAction>
<className>fr.adrienbrault.idea.symfony2plugin.intentions.php.PhpServiceIntention</className>
<category>PHP</category>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<html>
<body>
<!-- tooltip end -->
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package fr.adrienbrault.idea.symfony2plugin.tests.dic.inspection;

import fr.adrienbrault.idea.symfony2plugin.dic.inspection.ServiceNamedArgumentExistsInspection;
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class ServiceNamedArgumentExistsInspectionTest extends SymfonyLightCodeInsightFixtureTestCase {
public void setUp() throws Exception {
super.setUp();
myFixture.copyFileToProject("classes.php");
myFixture.copyFileToProject("services.xml");
}

public String getTestDataPath() {
return "src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/dic/inspection/fixtures";
}

public void testMissingArgumentForYaml() {
assertLocalInspectionContains("foo.yml",
"Foobar\\NamedArgument:\n" +
" arguments:\n" +
" $foo<caret>bar1: ~",
ServiceNamedArgumentExistsInspection.INSPECTION_MESSAGE
);

assertLocalInspectionNotContains("foo.yml",
"Foobar\\UnknownClassNamedArgument:\n" +
" arguments:\n" +
" $foo<caret>bar: ~",
ServiceNamedArgumentExistsInspection.INSPECTION_MESSAGE
);
}

public void testMissingArgumentForFactoryServiceIsNotTriggeredYaml() {
assertLocalInspectionNotContains("foo.yml",
"Foobar\\NamedArgument:\n" +
" factory: ~\n" +
" arguments:\n" +
" $foo<caret>bar1: ~",
ServiceNamedArgumentExistsInspection.INSPECTION_MESSAGE
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,11 @@ class Car
{
const FOOBAR = null;
}

class NamedArgument
{
public function __construct($foobar)
{
}
}
}