public void testNullCoalescingAndConditionalOps() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@param pa: unknown}", "{@param pi: int}", "{@param pf: float}", "{@param? ni: int}", "{captureType($pa ?: $pi)}", "{captureType($pi ?: $pf)}", "{captureType($pa ? $pi : $pf)}", "{captureType($ni ?: 0)}")) .declaredSyntaxVersion(SyntaxVersion.V2_0) .doRunInitialParsingPasses(false) .typeRegistry(typeRegistry) .parse() .fileSet(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getCapturedTypes(soyTree); assertThat(types.get(0)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(1)) .isEqualTo(UnionType.of(IntType.getInstance(), FloatType.getInstance())); assertThat(types.get(2)) .isEqualTo(UnionType.of(IntType.getInstance(), FloatType.getInstance())); assertThat(types.get(3)).isEqualTo(IntType.getInstance()); }
public void testParseUnionTypes() { assertTypeEquals(UnionType.of(IntType.getInstance(), BoolType.getInstance()), "int|bool"); assertTypeEquals( UnionType.of(IntType.getInstance(), BoolType.getInstance(), StringType.getInstance()), "int|bool|string"); assertTypeEquals(UnionType.of(IntType.getInstance(), BoolType.getInstance()), " int | bool "); }
public void testArithmeticOps() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@param pa: unknown}", "{@param pi: int}", "{@param pf: float}", "{@param ps: string}", "{captureType($pa + $pa)}", "{captureType($pi + $pi)}", "{captureType($pf + $pf)}", "{captureType($pa - $pa)}", "{captureType($pi - $pi)}", "{captureType($pf - $pf)}", "{captureType($pa * $pa)}", "{captureType($pi * $pi)}", "{captureType($pf * $pf)}", "{captureType($pa / $pa)}", "{captureType($pi / $pi)}", "{captureType($pf / $pf)}", "{captureType($pa % $pa)}", "{captureType($pi % $pi)}", "{captureType($pf % $pf)}", "{captureType(-$pa)}", "{captureType(-$pi)}", "{captureType(-$pf)}", // The remainder are all logically template errors but are not enforced by the // compiler "{captureType(-$ps)}", "{captureType($ps / $pf)}")) .declaredSyntaxVersion(SyntaxVersion.V2_0) .typeRegistry(typeRegistry) .parse() .fileSet(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getCapturedTypes(soyTree); assertThat(types.get(0)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(1)).isEqualTo(IntType.getInstance()); assertThat(types.get(2)).isEqualTo(FloatType.getInstance()); assertThat(types.get(3)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(4)).isEqualTo(IntType.getInstance()); assertThat(types.get(5)).isEqualTo(FloatType.getInstance()); assertThat(types.get(6)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(7)).isEqualTo(IntType.getInstance()); assertThat(types.get(8)).isEqualTo(FloatType.getInstance()); assertThat(types.get(9)).isEqualTo(FloatType.getInstance()); assertThat(types.get(10)).isEqualTo(FloatType.getInstance()); assertThat(types.get(11)).isEqualTo(FloatType.getInstance()); assertThat(types.get(12)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(13)).isEqualTo(IntType.getInstance()); assertThat(types.get(14)).isEqualTo(FloatType.getInstance()); assertThat(types.get(15)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(16)).isEqualTo(IntType.getInstance()); assertThat(types.get(17)).isEqualTo(FloatType.getInstance()); // These are the 'error' cases assertThat(types.get(18)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(19)).isEqualTo(UnknownType.getInstance()); }
public void testDataRefTypes() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@param pa: bool}", "{@param pb: list<int>}", "{@param pe: map<int, map<int, string>>}", "{captureType($pa)}", "{captureType($pb)}", "{captureType($pb[0])}", "{captureType($pe)}", "{captureType($pe[0])}", "{captureType($pe[1 + 1][2])}")) .parse() .fileSet(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getCapturedTypes(soyTree); assertThat(types.get(0)).isEqualTo(BoolType.getInstance()); assertThat(types.get(1)).isEqualTo(ListType.of(IntType.getInstance())); assertThat(types.get(2)).isEqualTo(IntType.getInstance()); assertThat(types.get(3)) .isEqualTo( MapType.of( IntType.getInstance(), MapType.of(IntType.getInstance(), StringType.getInstance()))); assertThat(types.get(4)).isEqualTo(MapType.of(IntType.getInstance(), StringType.getInstance())); assertThat(types.get(5)).isEqualTo(StringType.getInstance()); }
public void testParseTypeNames() { assertTypeEquals(AnyType.getInstance(), "any"); assertTypeEquals(AnyType.getInstance(), " any "); assertTypeEquals(IntType.getInstance(), "int"); assertTypeEquals(BoolType.getInstance(), "bool"); assertTypeEquals(FOO_BAR_TYPE, "foo.bar"); assertTypeEquals(FOO_BAR_TYPE, " foo.bar "); assertTypeEquals(FOO_BAR_TYPE, " foo . bar "); assertTypeEquals(UnknownType.getInstance(), "?"); }
public void testListLiteral() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@param pi: int}", "{@param pf: float}", "{let $list: [$pi, $pf]/}", "{$list}", "{$list.length}")) .declaredSyntaxVersion(SyntaxVersion.V2_0) .doRunInitialParsingPasses(false) .typeRegistry(typeRegistry) .parse(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getPrintStatementTypes(soyTree); assertThat(types.get(0)) .isEqualTo(ListType.of(UnionType.of(IntType.getInstance(), FloatType.getInstance()))); assertThat(types.get(1)).isEqualTo(IntType.getInstance()); }
public void testParseRecordTypes() { assertTypeEquals( RecordType.of( ImmutableMap.<String, SoyType>builder().put("a", IntType.getInstance()).build()), "[a:int]"); assertTypeEquals( RecordType.of( ImmutableMap.<String, SoyType>builder() .put("a", IntType.getInstance()) .put("b", FloatType.getInstance()) .build()), "[a:int, b:float]"); assertTypeEquals( RecordType.of( ImmutableMap.<String, SoyType>builder() .put("a", IntType.getInstance()) .put("b", ListType.of(StringType.getInstance())) .build()), "[a:int, b:list<string>]"); }
public void testRecordTypes() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource("{@param pa: [a:int, b:string]}", "{$pa.a}", "{$pa.b}")) .parse(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getPrintStatementTypes(soyTree); assertThat(types.get(0)).isEqualTo(IntType.getInstance()); assertThat(types.get(1)).isEqualTo(StringType.getInstance()); }
public void testMapLiteral() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@param pi: int}", "{@param pf: float}", "{let $map: [1: $pi, 2:$pf]/}", "{captureType($map)}")) .declaredSyntaxVersion(SyntaxVersion.V2_0) .typeRegistry(typeRegistry) .parse() .fileSet(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); SoyType type = Iterables.getOnlyElement(getCapturedTypes(soyTree)); assertThat(type) .isEqualTo( MapType.of( IntType.getInstance(), UnionType.of(IntType.getInstance(), FloatType.getInstance()))); }
public void testDataFlowTypeNarrowing_complexExpressions() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@param map: map<string, int|null>}", "{@param record: [a : [nullableInt : int|null, nullableBool : bool|null]|null]}", "{@param pb: bool}", "{if $map['a']}", " {$map['a']}", "{/if}", "{if $record.a?.nullableInt}", " {$record.a?.nullableInt}", "{/if}", "")) .parse(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getPrintStatementTypes(soyTree); assertThat(types.get(0)).isEqualTo(IntType.getInstance()); assertThat(types.get(1)).isEqualTo(IntType.getInstance()); }
public void testOptionalParamTypes() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@param? pa: bool}", "{@param? pb: list<int>}", "{$pa}", "{$pb}")) .parse(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getPrintStatementTypes(soyTree); assertThat(types.get(0)).isEqualTo(makeNullable(BoolType.getInstance())); assertThat(types.get(1)).isEqualTo(makeNullable(ListType.of(IntType.getInstance()))); }
public void testNullCoalescingAndConditionalOps_complexCondition() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource("{@param? l: [a :int]}", "{$l?.a ?: 0}")) .declaredSyntaxVersion(SyntaxVersion.V2_0) .doRunInitialParsingPasses(false) .typeRegistry(typeRegistry) .parse(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getPrintStatementTypes(soyTree); assertThat(types.get(0)).isEqualTo(IntType.getInstance()); }
public void testConditionalOperatorDataFlowTypeNarrowing() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@param pa: bool|null}", "{@param pb: bool}", "{@param pc: [a : int|null]}", "{$pa ? $pa : $pb}", // #0 must be non-null "{$pa != null ?: $pb}", // #1 must be non-null "{$pa ?: $pb}", "{$pc.a ? $pc.a : 0}", "{if not $pc.a}{$pc.a}{/if}")) .parse(); // #2 must be non-null (re-written to (isNonnull($pa) ? $pa : $pb)) createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getPrintStatementTypes(soyTree); assertThat(types.get(0)).isEqualTo(BoolType.getInstance()); assertThat(types.get(1)).isEqualTo(BoolType.getInstance()); assertThat(types.get(2)).isEqualTo(BoolType.getInstance()); assertThat(types.get(3)).isEqualTo(IntType.getInstance()); assertThat(types.get(4)).isEqualTo(makeNullable(IntType.getInstance())); }
public void testFunctionTyping() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@inject list: list<int|null>}", "{foreach $item in $list}", " {index($item)}", " {isLast($item)}", " {isFirst($item)}", " {$item}", " {checkNotNull($item)}", "{/foreach}")) .parse(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getPrintStatementTypes(soyTree); assertThat(types.get(0)).isEqualTo(IntType.getInstance()); assertThat(types.get(1)).isEqualTo(BoolType.getInstance()); assertThat(types.get(2)).isEqualTo(BoolType.getInstance()); assertThat(types.get(3)).isEqualTo(makeNullable(IntType.getInstance())); assertThat(types.get(4)).isEqualTo(IntType.getInstance()); }
public void testInjectedParamTypes() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@inject pa: bool}", "{@inject? pb: list<int>}", "{captureType($pa)}", "{captureType($pb)}")) .parse() .fileSet(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getCapturedTypes(soyTree); assertThat(types.get(0)).isEqualTo(BoolType.getInstance()); assertThat(types.get(1)).isEqualTo(makeNullable(ListType.of(IntType.getInstance()))); }
public void testArithmeticOps() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@param pa: unknown}", "{@param pi: int}", "{@param pf: float}", "{$pa + $pa}", "{$pi + $pi}", "{$pf + $pf}", "{$pa - $pa}", "{$pi - $pi}", "{$pf - $pf}", "{$pa * $pa}", "{$pi * $pi}", "{$pf * $pf}", "{$pa / $pa}", "{$pi / $pi}", "{$pf / $pf}", "{$pa % $pa}", "{$pi % $pi}", "{$pf % $pf}", "{-$pa}", "{-$pi}", "{-$pf}")) .declaredSyntaxVersion(SyntaxVersion.V2_0) .doRunInitialParsingPasses(false) .typeRegistry(typeRegistry) .parse(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getPrintStatementTypes(soyTree); assertThat(types.get(0)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(1)).isEqualTo(IntType.getInstance()); assertThat(types.get(2)).isEqualTo(FloatType.getInstance()); assertThat(types.get(3)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(4)).isEqualTo(IntType.getInstance()); assertThat(types.get(5)).isEqualTo(FloatType.getInstance()); assertThat(types.get(6)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(7)).isEqualTo(IntType.getInstance()); assertThat(types.get(8)).isEqualTo(FloatType.getInstance()); assertThat(types.get(9)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(10)).isEqualTo(IntType.getInstance()); assertThat(types.get(11)).isEqualTo(FloatType.getInstance()); assertThat(types.get(12)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(13)).isEqualTo(IntType.getInstance()); assertThat(types.get(14)).isEqualTo(FloatType.getInstance()); assertThat(types.get(15)).isEqualTo(UnknownType.getInstance()); assertThat(types.get(16)).isEqualTo(IntType.getInstance()); assertThat(types.get(17)).isEqualTo(FloatType.getInstance()); }
public void testMapLiteralAsRecord() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@param pi: int}", "{@param pf: float}", "{let $map: ['a': $pi, 'b':$pf]/}", "{captureType($map)}")) .declaredSyntaxVersion(SyntaxVersion.V2_0) .typeRegistry(typeRegistry) .parse() .fileSet(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); List<SoyType> types = getCapturedTypes(soyTree); assertThat(types.get(0)) .isEqualTo( RecordType.of( ImmutableMap.<String, SoyType>of( "a", IntType.getInstance(), "b", FloatType.getInstance()))); }
public void testMapLiteralWithStringKeysAsMap() { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents( constructTemplateSource( "{@param v1: int}", "{@param v2: string}", "{@param k1: string}", "{let $map: [$k1: $v1, 'b': $v2] /}", "{$map}")) .declaredSyntaxVersion(SyntaxVersion.V2_0) .doRunInitialParsingPasses(false) .typeRegistry(typeRegistry) .parse(); createResolveNamesVisitorForMaxSyntaxVersion().exec(soyTree); createResolveExpressionTypesVisitorForMaxSyntaxVersion().exec(soyTree); SoyType type = Iterables.getOnlyElement(getPrintStatementTypes(soyTree)); assertThat(type) .isEqualTo( MapType.of( StringType.getInstance(), UnionType.of(StringType.getInstance(), IntType.getInstance()))); }
public void testParameterizedTypes() { assertTypeEquals(ListType.of(StringType.getInstance()), "list<string>"); assertTypeEquals(ListType.of(StringType.getInstance()), "list < string > "); assertTypeEquals(MapType.of(IntType.getInstance(), BoolType.getInstance()), "map<int, bool>"); }