Skip to content

Commit

Permalink
Support @param default initializers
Browse files Browse the repository at this point in the history
Fixes the rest of google#172
  • Loading branch information
Alexander Pavlov committed Jan 3, 2020
1 parent 40a2a96 commit df0d4f9
Show file tree
Hide file tree
Showing 20 changed files with 444 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/main/grammars/Soy.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ AtParamSingle ::= <<BracedTag AtParamBody>> {
hooks = [wsBinders = "LEADING_COMMENTS_BINDER, TRAILING_COMMENTS_BINDER"]
}

private AtParamBody ::= (AT_PARAM | AT_PARAM_OPT) ParamDefinitionIdentifier COLON TypeExpression {
private AtParamBody ::= (AT_PARAM | AT_PARAM_OPT) ParamDefinitionIdentifier ((COLON TypeExpression [EQUAL Expr]) | COLON_EQUAL Expr) {
pin = 1
recoverWhile = "recoverEndOfTag"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package com.google.bamboo.soy.elements;

import com.google.bamboo.soy.lang.Parameter;
import com.google.bamboo.soy.parser.SoyExpr;
import com.google.bamboo.soy.parser.SoyParamDefinitionIdentifier;
import com.google.bamboo.soy.parser.SoyTypeExpression;
import com.google.bamboo.soy.parser.SoyTypes;
Expand All @@ -27,14 +28,17 @@
import org.jetbrains.annotations.Nullable;

public interface AtParamElement extends StubBasedPsiElement<AtParamStub>, PsiNamedElement,
TagElement {
TagElement, DefaultInitializerAware {

@Nullable
SoyParamDefinitionIdentifier getParamDefinitionIdentifier();

@Nullable
SoyTypeExpression getTypeExpression();

@Nullable
SoyExpr getExpr();

default PsiElement setName(@NotNull String s) throws IncorrectOperationException {
return null;
}
Expand All @@ -57,6 +61,10 @@ default boolean isOptional() {
return getTagNameTokenType() == SoyTypes.AT_PARAM_OPT;
}

default SoyExpr getDefaultInitializerExpr() {
return getExpr();
}

default Parameter toParameter() {
return this.getParamDefinitionIdentifier() == null
? null
Expand Down
13 changes: 9 additions & 4 deletions src/main/java/com/google/bamboo/soy/elements/AtStateElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@

package com.google.bamboo.soy.elements;

import com.google.bamboo.soy.lang.Parameter;
import com.google.bamboo.soy.lang.StateVariable;
import com.google.bamboo.soy.parser.SoyExpr;
import com.google.bamboo.soy.parser.SoyParamDefinitionIdentifier;
import com.google.bamboo.soy.parser.SoyTypeExpression;
import com.google.bamboo.soy.parser.SoyTypes;
import com.google.bamboo.soy.stubs.AtParamStub;
import com.google.bamboo.soy.stubs.AtStateStub;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
Expand All @@ -29,14 +27,17 @@
import org.jetbrains.annotations.Nullable;

public interface AtStateElement extends StubBasedPsiElement<AtStateStub>, PsiNamedElement,
TagElement {
TagElement, DefaultInitializerAware {

@Nullable
SoyParamDefinitionIdentifier getParamDefinitionIdentifier();

@Nullable
SoyTypeExpression getTypeExpression();

@Nullable
SoyExpr getExpr();

default PsiElement setName(@NotNull String s) throws IncorrectOperationException {
return null;
}
Expand All @@ -52,6 +53,10 @@ default String getType() {
return "";
}

default SoyExpr getDefaultInitializerExpr() {
return getExpr();
}

default StateVariable toStateVariable() {
return this.getParamDefinitionIdentifier() == null
? null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.bamboo.soy.elements;

import com.google.bamboo.soy.parser.SoyExpr;

public interface DefaultInitializerAware extends TagElement {

SoyExpr getDefaultInitializerExpr();
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import com.google.bamboo.soy.lang.Parameter;
import com.google.bamboo.soy.lang.Scope;
import com.google.bamboo.soy.lang.StateVariable;
import com.google.bamboo.soy.lang.Variable;
import com.google.bamboo.soy.parser.SoyAtInjectSingle;
import com.google.bamboo.soy.parser.SoyAtParamSingle;
Expand Down Expand Up @@ -76,6 +77,18 @@ default List<Parameter> getParameters() {
.collect(Collectors.toList());
}

@NotNull
default List<StateVariable> getStates() {
if (getStub() != null) {
return getStub().getStates();
}
return getAtStateSingleList()
.stream()
.map(SoyAtStateSingle::toStateVariable)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}

@Nullable
@Override
default Scope getParentScope() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.bamboo.soy.insight.annotators;

import com.google.bamboo.soy.parser.SoyAtParamSingle;
import com.google.bamboo.soy.parser.SoyTypes;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;

public class DefaultInitializerOnOptionalParameterAnnotator implements Annotator {

@Override
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder annotationHolder) {
if (element instanceof SoyAtParamSingle) {
SoyAtParamSingle atParamSingle = (SoyAtParamSingle) element;
if (atParamSingle.getTagNameTokenType() == SoyTypes.AT_PARAM_OPT
&& atParamSingle.getExpr() != null) {
annotationHolder.createErrorAnnotation(atParamSingle,
"Default initializers are not supported on optional parameters. Did you mean '{@param "
+ atParamSingle.getName() + " ...}'?");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.bamboo.soy.insight.annotators;

import com.google.bamboo.soy.elements.DefaultInitializerAware;
import com.google.bamboo.soy.lang.ParamUtils;
import com.google.bamboo.soy.lang.StateVariable;
import com.google.bamboo.soy.lang.Variable;
import com.google.bamboo.soy.parser.SoyAtParamSingle;
import com.google.bamboo.soy.parser.SoyExpr;
import com.google.bamboo.soy.parser.SoyTypes;
import com.google.bamboo.soy.parser.SoyVariableReferenceIdentifier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import java.util.Collection;
import org.jetbrains.annotations.NotNull;

public class DefaultInitializerRefsAnnotator implements Annotator {

@Override
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder annotationHolder) {
if (!(element instanceof SoyVariableReferenceIdentifier)) {
return;
}
SoyVariableReferenceIdentifier variableRef = (SoyVariableReferenceIdentifier) element;

DefaultInitializerAware atParamOrState = PsiTreeUtil
.getParentOfType(variableRef, DefaultInitializerAware.class);
if (atParamOrState == null) {
return;
}
SoyExpr atDefaultInitializer = atParamOrState.getDefaultInitializerExpr();
if (atDefaultInitializer == null) {
return;
}

if (PsiTreeUtil.findFirstParent(
element, el -> el == atDefaultInitializer) == null) {
// element is not a child of a SoyAt[State|Param]Single's default initializer Expr.
return;
}

if (atParamOrState instanceof SoyAtParamSingle) {
annotationHolder.createErrorAnnotation(element,
"Default initializers cannot depend on other parameters or state");
return;
}

Collection<StateVariable> declaredStates = ParamUtils.getStateDefinitions(element);

if (variableRef.getIdentifierWord() == null) {
return;
}

String variableName = variableRef.getIdentifierWord().getText();
for (StateVariable stateVariable : declaredStates) {
if (variableName.equals(stateVariable.name)) {
annotationHolder.createErrorAnnotation(element,
"State cannot be referenced in default initializers");
return;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import com.google.bamboo.soy.lang.Parameter;
import com.google.bamboo.soy.lang.Variable;
import com.google.bamboo.soy.parser.SoyTemplateBlock;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.psi.PsiElement;
Expand All @@ -40,32 +42,40 @@ public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder anno
return;
}

Collection<Parameter> parameters = ParamUtils.getParamDefinitions(element);
Collection<? extends Variable> variables = Streams
.concat(ParamUtils.getParamDefinitions(element).stream(),
ParamUtils.getStateDefinitions(element).stream()).collect(
ImmutableList.toImmutableList());

Collection<String> usedVariableIdentifiers =
PsiTreeUtil.findChildrenOfType(element, IdentifierElement.class)
.stream()
.map(IdentifierElement::getReferences)
.flatMap(references -> Arrays.stream(references))
.flatMap(Arrays::stream)
.map(PsiReference::getCanonicalText)
.filter(id -> id.startsWith("$"))
.map(id -> id.substring(1))
.distinct()
.collect(Collectors.toList());

for (Variable parameter : parameters) {
for (Variable variable : variables) {
boolean isMatched = false;
for (String usedIdentifier : usedVariableIdentifiers) {
if (usedIdentifier.startsWith(parameter.name)) {
if (usedIdentifier.startsWith(variable.name)) {
isMatched = true;
break;
}
}

if (!isMatched) {
annotationHolder.createInfoAnnotation(parameter.element,
"Parameter " + parameter.name + " is unused.");
annotationHolder.createInfoAnnotation(variable.element,
variableType(variable) + " " + variable.name + " is unused.");
}
}
}
}

private static String variableType(Variable variable) {
return variable instanceof Parameter ? "Parameter" : "State variable";
}
}
Loading

0 comments on commit df0d4f9

Please sign in to comment.