Skip to content

Commit 9ad241f

Browse files
authored
Improve accuracy of reported semantic tokens for type declarations (#102)
* decoder: refactor existing type declaration test * decoder: add tests for unknown type declarations * decoder: avoid reporting invalid types as semantic tokens
1 parent 96d2cc1 commit 9ad241f

File tree

3 files changed

+348
-351
lines changed

3 files changed

+348
-351
lines changed

decoder/semantic_tokens.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func (d *PathDecoder) tokensForExpression(expr hclsyntax.Expression, constraints
223223
}
224224
}
225225
_, ok = constraints.TypeDeclarationExpr()
226-
if ok {
226+
if ok && isPrimitiveTypeDeclaration(exprKeyword) {
227227
tokens = append(tokens, lang.SemanticToken{
228228
Type: lang.TokenTypePrimitive,
229229
Modifiers: []lang.SemanticTokenModifier{},
@@ -232,7 +232,7 @@ func (d *PathDecoder) tokensForExpression(expr hclsyntax.Expression, constraints
232232
}
233233
case *hclsyntax.FunctionCallExpr:
234234
_, ok := constraints.TypeDeclarationExpr()
235-
if ok {
235+
if ok && isComplexTypeDeclaration(eType.Name) {
236236
tokens = append(tokens, lang.SemanticToken{
237237
Type: lang.TokenTypeCapsule,
238238
Modifiers: []lang.SemanticTokenModifier{},
@@ -365,6 +365,38 @@ func (d *PathDecoder) tokensForExpression(expr hclsyntax.Expression, constraints
365365
return tokens
366366
}
367367

368+
func isPrimitiveTypeDeclaration(kw string) bool {
369+
switch kw {
370+
case "bool":
371+
return true
372+
case "number":
373+
return true
374+
case "string":
375+
return true
376+
case "null":
377+
return true
378+
case "any":
379+
return true
380+
}
381+
return false
382+
}
383+
384+
func isComplexTypeDeclaration(funcName string) bool {
385+
switch funcName {
386+
case "list":
387+
return true
388+
case "set":
389+
return true
390+
case "tuple":
391+
return true
392+
case "map":
393+
return true
394+
case "object":
395+
return true
396+
}
397+
return false
398+
}
399+
368400
func (d *PathDecoder) tokensForObjectConsTypeDeclarationExpr(expr *hclsyntax.ObjectConsExpr, constraints ExprConstraints) []lang.SemanticToken {
369401
tokens := make([]lang.SemanticToken, 0)
370402
for _, item := range expr.Items {

decoder/semantic_tokens_expr_test.go

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,3 +1943,317 @@ func TestDecoder_SemanticTokensInFile_traversalExpression(t *testing.T) {
19431943
})
19441944
}
19451945
}
1946+
1947+
func TestDecoder_SemanticTokensInFile_typeDeclaration(t *testing.T) {
1948+
testCases := []struct {
1949+
name string
1950+
attrSchema map[string]*schema.AttributeSchema
1951+
cfg string
1952+
expectedTokens []lang.SemanticToken
1953+
}{
1954+
{
1955+
"known primitive type",
1956+
map[string]*schema.AttributeSchema{
1957+
"type": {
1958+
Expr: schema.ExprConstraints{schema.TypeDeclarationExpr{}},
1959+
},
1960+
},
1961+
`type = string`,
1962+
[]lang.SemanticToken{
1963+
{
1964+
Type: lang.TokenAttrName,
1965+
Modifiers: []lang.SemanticTokenModifier{},
1966+
Range: hcl.Range{
1967+
Filename: "test.tf",
1968+
Start: hcl.Pos{
1969+
Line: 1,
1970+
Column: 1,
1971+
Byte: 0,
1972+
},
1973+
End: hcl.Pos{
1974+
Line: 1,
1975+
Column: 5,
1976+
Byte: 4,
1977+
},
1978+
},
1979+
},
1980+
{
1981+
Type: lang.TokenTypePrimitive,
1982+
Modifiers: []lang.SemanticTokenModifier{},
1983+
Range: hcl.Range{
1984+
Filename: "test.tf",
1985+
Start: hcl.Pos{
1986+
Line: 1,
1987+
Column: 8,
1988+
Byte: 7,
1989+
},
1990+
End: hcl.Pos{
1991+
Line: 1,
1992+
Column: 14,
1993+
Byte: 13,
1994+
},
1995+
},
1996+
},
1997+
},
1998+
},
1999+
{
2000+
"unknown primitive type",
2001+
map[string]*schema.AttributeSchema{
2002+
"type": {
2003+
Expr: schema.ExprConstraints{schema.TypeDeclarationExpr{}},
2004+
},
2005+
},
2006+
`type = foobar`,
2007+
[]lang.SemanticToken{
2008+
{
2009+
Type: lang.TokenAttrName,
2010+
Modifiers: []lang.SemanticTokenModifier{},
2011+
Range: hcl.Range{
2012+
Filename: "test.tf",
2013+
Start: hcl.Pos{
2014+
Line: 1,
2015+
Column: 1,
2016+
Byte: 0,
2017+
},
2018+
End: hcl.Pos{
2019+
Line: 1,
2020+
Column: 5,
2021+
Byte: 4,
2022+
},
2023+
},
2024+
},
2025+
},
2026+
},
2027+
{
2028+
"known collection type",
2029+
map[string]*schema.AttributeSchema{
2030+
"type": {
2031+
Expr: schema.ExprConstraints{schema.TypeDeclarationExpr{}},
2032+
},
2033+
},
2034+
`type = list(any)`,
2035+
[]lang.SemanticToken{
2036+
{
2037+
Type: lang.TokenAttrName,
2038+
Modifiers: []lang.SemanticTokenModifier{},
2039+
Range: hcl.Range{
2040+
Filename: "test.tf",
2041+
Start: hcl.Pos{
2042+
Line: 1,
2043+
Column: 1,
2044+
Byte: 0,
2045+
},
2046+
End: hcl.Pos{
2047+
Line: 1,
2048+
Column: 5,
2049+
Byte: 4,
2050+
},
2051+
},
2052+
},
2053+
{
2054+
Type: lang.TokenTypeCapsule,
2055+
Modifiers: []lang.SemanticTokenModifier{},
2056+
Range: hcl.Range{
2057+
Filename: "test.tf",
2058+
Start: hcl.Pos{
2059+
Line: 1,
2060+
Column: 8,
2061+
Byte: 7,
2062+
},
2063+
End: hcl.Pos{
2064+
Line: 1,
2065+
Column: 12,
2066+
Byte: 11,
2067+
},
2068+
},
2069+
},
2070+
{
2071+
Type: lang.TokenTypePrimitive,
2072+
Modifiers: []lang.SemanticTokenModifier{},
2073+
Range: hcl.Range{
2074+
Filename: "test.tf",
2075+
Start: hcl.Pos{
2076+
Line: 1,
2077+
Column: 13,
2078+
Byte: 12,
2079+
},
2080+
End: hcl.Pos{
2081+
Line: 1,
2082+
Column: 16,
2083+
Byte: 15,
2084+
},
2085+
},
2086+
},
2087+
},
2088+
},
2089+
{
2090+
"unknown collection type",
2091+
map[string]*schema.AttributeSchema{
2092+
"type": {
2093+
Expr: schema.ExprConstraints{schema.TypeDeclarationExpr{}},
2094+
},
2095+
},
2096+
`type = foobar(any)`,
2097+
[]lang.SemanticToken{
2098+
{
2099+
Type: lang.TokenAttrName,
2100+
Modifiers: []lang.SemanticTokenModifier{},
2101+
Range: hcl.Range{
2102+
Filename: "test.tf",
2103+
Start: hcl.Pos{
2104+
Line: 1,
2105+
Column: 1,
2106+
Byte: 0,
2107+
},
2108+
End: hcl.Pos{
2109+
Line: 1,
2110+
Column: 5,
2111+
Byte: 4,
2112+
},
2113+
},
2114+
},
2115+
},
2116+
},
2117+
{
2118+
"known object type",
2119+
map[string]*schema.AttributeSchema{
2120+
"type": {
2121+
Expr: schema.ExprConstraints{schema.TypeDeclarationExpr{}},
2122+
},
2123+
},
2124+
`type = object({
2125+
enabled = bool
2126+
})`,
2127+
[]lang.SemanticToken{
2128+
{
2129+
Type: lang.TokenAttrName,
2130+
Modifiers: []lang.SemanticTokenModifier{},
2131+
Range: hcl.Range{
2132+
Filename: "test.tf",
2133+
Start: hcl.Pos{
2134+
Line: 1,
2135+
Column: 1,
2136+
Byte: 0,
2137+
},
2138+
End: hcl.Pos{
2139+
Line: 1,
2140+
Column: 5,
2141+
Byte: 4,
2142+
},
2143+
},
2144+
},
2145+
{
2146+
Type: lang.TokenTypeCapsule,
2147+
Modifiers: []lang.SemanticTokenModifier{},
2148+
Range: hcl.Range{
2149+
Filename: "test.tf",
2150+
Start: hcl.Pos{
2151+
Line: 1,
2152+
Column: 8,
2153+
Byte: 7,
2154+
},
2155+
End: hcl.Pos{
2156+
Line: 1,
2157+
Column: 14,
2158+
Byte: 13,
2159+
},
2160+
},
2161+
},
2162+
{
2163+
Type: lang.TokenAttrName,
2164+
Modifiers: []lang.SemanticTokenModifier{},
2165+
Range: hcl.Range{
2166+
Filename: "test.tf",
2167+
Start: hcl.Pos{
2168+
Line: 2,
2169+
Column: 3,
2170+
Byte: 18,
2171+
},
2172+
End: hcl.Pos{
2173+
Line: 2,
2174+
Column: 10,
2175+
Byte: 25,
2176+
},
2177+
},
2178+
},
2179+
{
2180+
Type: lang.TokenTypePrimitive,
2181+
Modifiers: []lang.SemanticTokenModifier{},
2182+
Range: hcl.Range{
2183+
Filename: "test.tf",
2184+
Start: hcl.Pos{
2185+
Line: 2,
2186+
Column: 13,
2187+
Byte: 28,
2188+
},
2189+
End: hcl.Pos{
2190+
Line: 2,
2191+
Column: 17,
2192+
Byte: 32,
2193+
},
2194+
},
2195+
},
2196+
},
2197+
},
2198+
{
2199+
"unknown object type",
2200+
map[string]*schema.AttributeSchema{
2201+
"type": {
2202+
Expr: schema.ExprConstraints{schema.TypeDeclarationExpr{}},
2203+
},
2204+
},
2205+
`type = foobar({
2206+
enabled = bool
2207+
})`,
2208+
[]lang.SemanticToken{
2209+
{
2210+
Type: lang.TokenAttrName,
2211+
Modifiers: []lang.SemanticTokenModifier{},
2212+
Range: hcl.Range{
2213+
Filename: "test.tf",
2214+
Start: hcl.Pos{
2215+
Line: 1,
2216+
Column: 1,
2217+
Byte: 0,
2218+
},
2219+
End: hcl.Pos{
2220+
Line: 1,
2221+
Column: 5,
2222+
Byte: 4,
2223+
},
2224+
},
2225+
},
2226+
},
2227+
},
2228+
}
2229+
2230+
for i, tc := range testCases {
2231+
t.Run(fmt.Sprintf("%d-%s", i, tc.name), func(t *testing.T) {
2232+
bodySchema := &schema.BodySchema{
2233+
Attributes: tc.attrSchema,
2234+
}
2235+
2236+
f, pDiags := hclsyntax.ParseConfig([]byte(tc.cfg), "test.tf", hcl.InitialPos)
2237+
if len(pDiags) > 0 {
2238+
t.Fatal(pDiags)
2239+
}
2240+
2241+
d := testPathDecoder(t, &PathContext{
2242+
Schema: bodySchema,
2243+
Files: map[string]*hcl.File{
2244+
"test.tf": f,
2245+
},
2246+
})
2247+
2248+
tokens, err := d.SemanticTokensInFile("test.tf")
2249+
if err != nil {
2250+
t.Fatal(err)
2251+
}
2252+
2253+
diff := cmp.Diff(tc.expectedTokens, tokens)
2254+
if diff != "" {
2255+
t.Fatalf("unexpected tokens: %s", diff)
2256+
}
2257+
})
2258+
}
2259+
}

0 commit comments

Comments
 (0)