| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package demangle |
| |
| import ( |
| "bufio" |
| "flag" |
| "fmt" |
| "os" |
| "strings" |
| "testing" |
| ) |
| |
| var verbose = flag.Bool("verbose", false, "print each demangle-expected symbol") |
| |
| const filename = "testdata/demangle-expected" |
| |
| // A list of exceptions from demangle-expected that we do not handle |
| // the same as the standard demangler. We keep a list of exceptions |
| // so that we can use an exact copy of the file. These exceptions are |
| // all based on different handling of a substitution that refers to a |
| // template parameter. The standard demangler seems to have a bug in |
| // which template it uses when a reference or rvalue-reference refers |
| // to a substitution that resolves to a template parameter. |
| var exceptions = map[string]bool{ |
| "_ZSt7forwardIRN1x14refobjiteratorINS0_3refINS0_4mime30multipart_section_processorObjIZ15get_body_parserIZZN14mime_processor21make_section_iteratorERKNS2_INS3_10sectionObjENS0_10ptrrefBaseEEEbENKUlvE_clEvEUlSB_bE_ZZNS6_21make_section_iteratorESB_bENKSC_clEvEUlSB_E0_ENS1_INS2_INS0_20outputrefiteratorObjIiEES8_EEEERKSsSB_OT_OT0_EUlmE_NS3_32make_multipart_default_discarderISP_EEEES8_EEEEEOT_RNSt16remove_referenceISW_E4typeE": true, |
| "_ZN3mdr16in_cached_threadIRZNK4cudr6GPUSet17parallel_for_eachIZN5tns3d20shape_representation7compute7GPUImpl7executeERKNS_1AINS_7ptr_refIKjEELl3ELl3ENS_8c_strideILl1ELl0EEEEERKNS8_INS9_IjEELl4ELl1ESD_EEEUliRKNS1_7ContextERNS7_5StateEE_JSt6vectorISO_SaISO_EEEEEvOT_DpRT0_EUlSP_E_JSt17reference_wrapperISO_EEEENS_12ScopedFutureIDTclfp_spcl7forwardISW_Efp0_EEEEESV_DpOSW_": true, |
| "_ZNSt9_Any_data9_M_accessIPZN3sel8Selector6SetObjI3FooJPKcMS4_FviEEEEvRT_DpT0_EUlvE_EESA_v": true, |
| "_ZNSt9_Any_data9_M_accessIPZN13ThreadManager7newTaskIRSt5_BindIFSt7_Mem_fnIM5DiaryFivEEPS5_EEIEEESt6futureINSt9result_ofIFT_DpT0_EE4typeEEOSF_DpOSG_EUlvE_EERSF_v": true, |
| "_ZNSt9_Any_data9_M_accessIPZN6cereal18polymorphic_detail15getInputBindingINS1_16JSONInputArchiveEEENS1_6detail15InputBindingMapIT_E11SerializersERS7_jEUlPvRSt10unique_ptrIvNS5_12EmptyDeleterIvEEEE0_EESA_v": true, |
| "_ZNSt9_Any_data9_M_accessIPZ4postISt8functionIFvvEEEvOT_EUlvE_EERS5_v": true, |
| "_ZNSt9_Any_data9_M_accessIPZN13ThreadManager10futureTaskISt5_BindIFSt7_Mem_fnIM6RunnerFvvEEPS5_EEEEvOT_EUlvE_EERSC_v": true, |
| } |
| |
| // For simplicity, this test reads an exact copy of |
| // libiberty/testsuite/demangle-expected from GCC. See that file for |
| // the syntax. We ignore all tests that are not --format=gnu-v3 or |
| // --format=auto with a string starting with _Z. |
| func TestExpected(t *testing.T) { |
| f, err := os.Open(filename) |
| if err != nil { |
| t.Fatal(err) |
| } |
| scanner := bufio.NewScanner(f) |
| lineno := 1 |
| for { |
| format, got := getOptLine(t, scanner, &lineno) |
| if !got { |
| break |
| } |
| report := lineno |
| input := getLine(t, scanner, &lineno) |
| expect := getLine(t, scanner, &lineno) |
| |
| testNoParams := false |
| skip := false |
| if len(format) > 0 && format[0] == '-' { |
| for _, arg := range strings.Fields(format) { |
| switch arg { |
| case "--format=gnu-v3": |
| case "--format=auto": |
| if !strings.HasPrefix(input, "_Z") { |
| skip = true |
| } |
| case "--no-params": |
| testNoParams = true |
| case "--ret-postfix", "--ret-drop": |
| skip = true |
| case "--is-v3-ctor", "--is-v3-dtor": |
| skip = true |
| default: |
| if !strings.HasPrefix(arg, "--format=") { |
| t.Errorf("%s:%d: unrecognized argument %s", filename, report, arg) |
| } |
| skip = true |
| } |
| } |
| } |
| |
| // The libiberty testsuite passes DMGL_TYPES to |
| // demangle type names, but that doesn't seem useful |
| // and we don't support it. |
| if !strings.HasPrefix(input, "_Z") && !strings.HasPrefix(input, "_GLOBAL_") { |
| skip = true |
| } |
| |
| var expectNoParams string |
| if testNoParams { |
| expectNoParams = getLine(t, scanner, &lineno) |
| } |
| |
| if skip { |
| continue |
| } |
| |
| oneTest(t, report, input, expect, true) |
| if testNoParams { |
| oneTest(t, report, input, expectNoParams, false) |
| } |
| } |
| if err := scanner.Err(); err != nil { |
| t.Error(err) |
| } |
| } |
| |
| // oneTest tests one entry from demangle-expected. |
| func oneTest(t *testing.T, report int, input, expect string, params bool) { |
| if *verbose { |
| fmt.Println(input) |
| } |
| |
| exception := exceptions[input] |
| |
| var s string |
| var err error |
| if params { |
| s, err = ToString(input) |
| } else { |
| s, err = ToString(input, NoParams) |
| } |
| if err != nil { |
| if exception { |
| t.Logf("%s:%d: ignore expected difference: got %q, expected %q", filename, report, err, expect) |
| return |
| } |
| |
| if err != ErrNotMangledName { |
| if input == expect { |
| return |
| } |
| t.Errorf("%s:%d: %v", filename, report, err) |
| return |
| } |
| s = input |
| } |
| |
| if s != expect { |
| if exception { |
| t.Logf("%s:%d: ignore expected difference: got %q, expected %q", filename, report, s, expect) |
| } else { |
| var a AST |
| if params { |
| a, err = ToAST(input) |
| } else { |
| a, err = ToAST(input, NoParams) |
| } |
| if err != nil { |
| t.Logf("ToAST error: %v", err) |
| } else { |
| t.Logf("\n%#v", a) |
| } |
| t.Errorf("%s:%d: params: %t: got %q, expected %q", filename, report, params, s, expect) |
| } |
| } else if exception && params { |
| t.Errorf("%s:%d: unexpected success (input listed in exceptions)", filename, report) |
| } |
| } |
| |
| // getLine reads a line from demangle-expected. |
| func getLine(t *testing.T, scanner *bufio.Scanner, lineno *int) string { |
| s, got := getOptLine(t, scanner, lineno) |
| if !got { |
| t.Fatalf("%s:%d: unexpected EOF", filename, *lineno) |
| } |
| return s |
| } |
| |
| // getOptLine reads an optional line from demangle-expected, returning |
| // false at EOF. It skips comment lines and updates *lineno. |
| func getOptLine(t *testing.T, scanner *bufio.Scanner, lineno *int) (string, bool) { |
| for { |
| if !scanner.Scan() { |
| return "", false |
| } |
| *lineno++ |
| line := scanner.Text() |
| if !strings.HasPrefix(line, "#") { |
| return line, true |
| } |
| } |
| } |