# Copyright 2017-2019 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see .
# Tests for function local static variables, both C and C++.
# This file is part of the gdb testsuite.
standard_testfile .c
# A few expected errors.
set syntax_re "A syntax error in expression, near.*"
set cannot_resolve_re "Cannot resolve method S::method to any overloaded instance"
# Build an "Cannot resolve method ..." expected error string for
# method METH.
#
proc cannot_resolve {meth} {
    return "Cannot resolve method $meth to any overloaded instance"
}
# A list of scopes that have the static variables that we want to
# print.  Each entry has, in order, the scope/function name, and the
# prefix used by the static variables.  The prefix exists both to make
# it easier to debug the test if something goes wrong, and, to make
# sure that printing the static local of one method overload doesn't
# find the variables of the wrong overload.
#
# While at it, we also try printing each scope without the static
# local, to check that the parse copes with cv overloads without
# quoting.  That's what the third and forth columns are for.  Note
# that printing "func()" is different from "func(void)".  The former
# is an inferior function call, while the latter is a reference to the
# function.
     #SCOPE				#PREFIX         #PRINT-SCOPE-QUOTED
							#PRINT-SCOPE-UNQUOTED (opt)
set cxx_scopes_list {
    {"S::method()"			"S_M"		{= \\{void \\(S \\* const\\)\\} $hex }
							{[cannot_resolve "S::method"]}}
    {"S::method() const"		"S_M_C"		{= \\{void \\(const S \\* const\\)\\} $hex }
							$syntax_re}
    {"S::method() volatile"		"S_M_V"		{= \\{void \\(volatile S \\* const\\)\\} $hex }
							$syntax_re}
    {"S::method() const volatile"	"S_M_CV"	{= \\{void \\(const volatile S \\* const\\)\\} $hex }
							$syntax_re}
    {"S::method() volatile const"	"S_M_CV"	{= \\{void \\(const volatile S \\* const\\)\\} $hex }
							$syntax_re}
    {"S::method(void)"			"S_M"		{= \\{void \\(S \\* const\\)\\} $hex }}
    {"S::method(void) const"		"S_M_C"		{= \\{void \\(const S \\* const\\)\\} $hex }}
    {"S::method(void) volatile"		"S_M_V"		{= \\{void \\(volatile S \\* const\\)\\} $hex }}
    {"S::method(void) const volatile"	"S_M_CV"	{= \\{void \\(const volatile S \\* const\\)\\} $hex }}
    {"S::method(void) volatile const"	"S_M_CV"	{= \\{void \\(const volatile S \\* const\\)\\} $hex }}
    {"S::static_method()"		"S_SM"		{= \\{void \\(void\\)\\} $hex }
							"void"}
    {"S::static_method(void)"		"S_SM"		{= \\{void \\(void\\)\\} $hex }}
    {"S::inline_method()"		"S_IM"		{= \\{void \\(S \\* const\\)\\} $hex }
							{[cannot_resolve "S::inline_method"]}}
    {"S::inline_method(void)"		"S_IM"		{= \\{void \\(S \\* const\\)\\} $hex }}
    {"S::static_inline_method()"	"S_SIM"		{= \\{void \\(void\\)\\} $hex }
							"void"}
    {"S::static_inline_method(void)"	"S_SIM"		{= \\{void \\(void\\)\\} $hex }}
    {"S2::method()"		"S2_M"		{= \\{void \\(S2 \\* const\\)\\} $hex ::method\\(\\)>}
							{[cannot_resolve "S2::method"]}}
    {"S2::static_method()"		"S2_SM"		{= \\{void \\(void\\)\\} $hex ::static_method\\(\\)>}
							"void"}
    {"S2::inline_method()"		"S2_IM"		{= \\{void \\(S2 \\* const\\)\\} $hex ::inline_method\\(\\)>}
							{[cannot_resolve "S2::inline_method"]}}
    {"S2::static_inline_method()"	"S2_SIM"	{= \\{void \\(void\\)\\} $hex ::static_inline_method\\(\\)>}
							"void"}
    {"free_func"			"FF"		{= \\{void \\(void\\)\\} $hex }}
    {"free_func()"			"FF"		{= \\{void \\(void\\)\\} $hex }
							"void"}
    {"free_func(void)"			"FF"		{= \\{void \\(void\\)\\} $hex }}
    {"free_inline_func()"		"FIF"		{= \\{void \\(void\\)\\} $hex }
							"void"}
    {"free_inline_func(void)"		"FIF"		{= \\{void \\(void\\)\\} $hex }}
}
set c_scopes_list {
    {"free_func"			"FF"		{= \\{void \\(void\\)\\} $hex }}
    {"free_inline_func"			"FIF"		{= \\{void \\(void\\)\\} $hex }}
}
# A list of all the static varibles defined in each scope.  The first
# column is the name of the variable, without the prefix, and the
# second column is a regex matching what printing the variable should
# output.
     #VAR		#PRINT
set vars_list {
    {"s_var_int"	" = 4"}
    {"s_var_float"	" = 3.14.*"}
    {"s_var_aggregate"	" = \\{i1 = 1, i2 = 2, i3 = 3\\}"}
}
proc do_test {lang} {
    global c_scopes_list
    global cxx_scopes_list
    global vars_list
    global srcfile testfile
    set options {debug}
    if {$lang == "c++"} {
	if { [skip_cplus_tests] } {
	    return
	}
	lappend options $lang
	set src ${srcfile}c
    } else {
	set src ${srcfile}
    }
    if {[prepare_for_testing "failed to prepare" $testfile-$lang \
	     [list $src] $options]} {
	return -1
    }
    if ![runto_main] then {
	fail "couldn't run to breakpoint"
	return
    }
    gdb_test "show language" " currently [string_to_regexp $lang]\"\\."
    if {$lang == "c"} {
	set scopes_list $c_scopes_list
    } else {
	set scopes_list $cxx_scopes_list
    }
    # Print each scope/function using these syntaxes:
    #
    #  "(gdb) p 'S::method() const'"    # quoted
    #  "(gdb) p S::method() const"      # unquoted
    #
    foreach scope_line $scopes_list  {
	set scope [lindex $scope_line 0]
	set print_quoted_re [lindex $scope_line 2]
	set print_quoted_re [uplevel 1 "subst -nobackslashes -nocommands \"$print_quoted_re\""]
	set print_unquoted_re [lindex $scope_line 3]
	set print_unquoted_re [uplevel 1 "subst -nobackslashes -nocommands \"$print_unquoted_re\""]
	gdb_test "print '${scope}'" $print_quoted_re
	if {$print_unquoted_re != ""} {
	    gdb_test "print ${scope}" $print_unquoted_re
	} else {
	    gdb_test "print ${scope}" $print_quoted_re
	}
    }
    # Print each variable using these syntaxes:
    #
    #  'func()'::var
    #  func()::var
    #  'func()::var'
    #
    # In C++, the latter case makes sure that symbol lookup finds the
    # debug symbol instead of the minimal symbol with that exact same
    # name.
    foreach scope_line $scopes_list  {
	set scope [lindex $scope_line 0]
	set var_prefix [lindex $scope_line 1]
	foreach var_line $vars_list {
	    set var [lindex $var_line 0]
	    set print_re [lindex $var_line 1]
	    gdb_test "print '${scope}'::${var_prefix}_${var}" $print_re
	    gdb_test "print ${scope}::${var_prefix}_${var}" $print_re
	    set sym "${scope}::${var_prefix}_${var}"
	    if {$lang == "c++"} {
		gdb_test "print '${sym}'" $print_re
	    } else {
		gdb_test "print '${sym}'" "No symbol \"$sym\" in current context\\."
	    }
	}
    }
    # Now run to each function, and print its variables using the
    # localy-visible name.
    foreach scope_line $scopes_list {
	set scope [lindex $scope_line 0]
	set var_prefix [lindex $scope_line 1]
	with_test_prefix "$scope" {
	    delete_breakpoints
	    gdb_breakpoint "$scope"
	    gdb_continue_to_breakpoint "$scope"
	    foreach var_line $vars_list {
		set var [lindex $var_line 0]
		set print_re [lindex $var_line 1]
		gdb_test "print ${var_prefix}_${var}" $print_re
	    }
	}
    }
}
foreach lang {"c" "c++"} {
    with_test_prefix $lang {
	do_test $lang
    }
}