@@ -98,6 +98,13 @@ func TestConstResolverValues(t *testing.T) {
9898 {"string of a code point" , `len(string(0x100))` , "2" },
9999 {"min of a typed argument" , "^max(2, uint8(1))" , "253" },
100100 {"max of a typed argument" , "min(uint8(7), 9) + 1" , "8" },
101+ {"max with an untyped float" , "max(5, 4.0) / 2 * 10" , "25" },
102+ {"min with an untyped float" , "min(5, 6.0) / 2 * 10" , "25" },
103+ {"max of integers only" , "max(5, 4) / 2 * 10" , "20" },
104+ {"string of a value out of range" , "len(string(-1))" , "3" },
105+ {"shift right of a wide value" , "1<<70 >> 2000" , "0" },
106+ // the compiler rejects a shift count this large outright, the result is still 0
107+ {"shift right past the bound" , "1 >> 2000000" , "0" },
101108 }
102109
103110 for _ , tt := range tests {
@@ -136,6 +143,29 @@ func TestConstResolverErrors(t *testing.T) {
136143 {"comparison operator" , "const x = 1 < 2" , "unsupported binary operator" },
137144 {"self reference" , "const x = x + 1" , "refers to itself" },
138145 {"reference cycle" , "const (\n \t x = y\n \t y = x\n )" , "refers to itself" },
146+ {"failing operand of a negation" , "const x = -missing" , "unknown constant missing" },
147+ {"complement of a string" , `const x = ^"str"` , "not an integer" },
148+ {"failing right operand" , "const x = 1 + missing" , "unknown constant missing" },
149+ {"operand out of range for the type" , "const x = uint8(1) + 300" , "overflows uint8" },
150+ {"string added to a number" , `const x = 1 + "s"` , "not a number" },
151+ {"number added to a string" , `const x = "s" + 1` , "not a number" },
152+ {"fractional remainder" , "const x = 1.5 % 2" , "not an integer" },
153+ {"remainder by a fraction" , "const x = 2 % 1.5" , "not an integer" },
154+ {"shift of a string" , `const x = "s" << 1` , "not an integer" },
155+ {"failing shift count" , "const x = 1 << missing" , "unknown constant missing" },
156+ {"fractional shift count" , "const x = 1 << 1.5" , "not an integer" },
157+ {"conversion with two arguments" , "const x = uint8(1, 2)" , "unsupported call expression" },
158+ {"min without arguments" , "const x = min()" , "at least one argument" },
159+ {"failing min argument" , "const x = min(missing, 1)" , "unknown constant missing" },
160+ {"min of a string" , `const x = min("a", 1)` , "not a number" },
161+ {"failing len argument" , "const x = len(missing)" , "unknown constant missing" },
162+ {"failing conversion argument" , "const x = uint8(missing)" , "unknown constant missing" },
163+ {"fractional conversion" , "const x = uint8(1.5)" , "not an integer" },
164+ {"float conversion of a string" , `const x = float64("s")` , "not a number" },
165+ {"string conversion of a fraction" , "const (\n \t x = str(1.5)\n )\n type str string" , "not a string" },
166+ {"call of an expression" , "const x = (1 + 1)(2)" , "unsupported call expression" },
167+ {"failing left operand of a typed sum" , "const x = 300 + uint8(1)" , "overflows uint8" },
168+ {"alias cycle" , "const x = ^a(0)\n type a = b\n type b = a" , "unsupported call to a" },
139169 }
140170
141171 for _ , tt := range tests {
@@ -322,6 +352,27 @@ func TestCheckIntRange(t *testing.T) {
322352 }
323353}
324354
355+ func TestConstResolverDeclarations (t * testing.T ) {
356+ // a name declared twice keeps the first declaration, which is what a compiling package has
357+ v , err := resolveSrc (t , "package p\n const x = 1\n const x = 2\n " , "x" )
358+ require .NoError (t , err )
359+ assert .Equal (t , "1" , v .ExactString ())
360+
361+ // specs that are not value or type declarations are skipped
362+ r := newConstResolver ()
363+ r .addFile (& ast.File {
364+ Name : & ast.Ident {Name : "p" },
365+ Decls : []ast.Decl {
366+ & ast.GenDecl {Tok : token .TYPE , Specs : []ast.Spec {& ast.ImportSpec {}}},
367+ & ast.GenDecl {Tok : token .CONST , Specs : []ast.Spec {& ast.ImportSpec {}}},
368+ & ast.GenDecl {Tok : token .IMPORT , Specs : []ast.Spec {& ast.ImportSpec {}}},
369+ & ast.FuncDecl {Name : & ast.Ident {Name : "f" }},
370+ },
371+ })
372+ assert .Empty (t , r .decls )
373+ assert .Empty (t , r .types )
374+ }
375+
325376func TestConstResolverUnsupportedNodes (t * testing.T ) {
326377 r := newConstResolver ()
327378
@@ -345,6 +396,18 @@ func TestConstResolverUnsupportedNodes(t *testing.T) {
345396 require .Error (t , err )
346397 assert .Contains (t , err .Error (), "not a number" )
347398
399+ for _ , lit := range []* ast.BasicLit {
400+ {Kind : token .INT , Value : "12abc" },
401+ {Kind : token .FLOAT , Value : "1.2.3" },
402+ {Kind : token .CHAR , Value : "'" },
403+ {Kind : token .CHAR , Value : "abc" },
404+ {Kind : token .CHAR , Value : `'\q'` },
405+ } {
406+ _ , err = literalValue (lit )
407+ require .Error (t , err , lit .Value )
408+ assert .Contains (t , err .Error (), "invalid literal" , lit .Value )
409+ }
410+
348411 _ , err = r .resolve ("nothing" )
349412 require .Error (t , err )
350413 assert .Contains (t , err .Error (), "unknown constant nothing" )
@@ -472,6 +535,32 @@ const (
472535 assert .Contains (t , err .Error (), "const codeB: value -1 is negative but the type is uint8" )
473536}
474537
538+ func TestParseUntypedValueOutOfRange (t * testing.T ) {
539+ // a constant without a type of its own still has to fit the underlying type of the enum
540+ src := `package test
541+ type small int8
542+ const (
543+ smallA small = 100
544+ smallB = 200
545+ )
546+ `
547+ _ , err := parseSrc (t , "small" , src )
548+ require .Error (t , err )
549+ assert .Contains (t , err .Error (), "const smallB: value 200 overflows int8" )
550+ }
551+
552+ func TestParseConstWithoutValue (t * testing.T ) {
553+ src := `package test
554+ type status int
555+ const (
556+ statusA
557+ )
558+ `
559+ _ , err := parseSrc (t , "status" , src )
560+ require .Error (t , err )
561+ assert .Contains (t , err .Error (), "no value for const statusA" )
562+ }
563+
475564func TestParseValueOutOfRange (t * testing.T ) {
476565 src := `package test
477566type small int8
0 commit comments