From 2c7e14a847318974490ab59460f0834ea2ee0d24 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 4 May 2018 18:03:46 +0200 Subject: gometalinter: enable cyclomatic complexity checking Refactor some functions to be simpler. Update #538 --- pkg/ast/scanner.go | 163 +++++++++++++++++++++++++++++---------------------- pkg/ifuzz/decode.go | 1 + pkg/ifuzz/encode.go | 1 + pkg/ifuzz/gen/gen.go | 156 ++++++++++++++++++++++++++---------------------- 4 files changed, 180 insertions(+), 141 deletions(-) (limited to 'pkg') diff --git a/pkg/ast/scanner.go b/pkg/ast/scanner.go index 343c3b9ab..67e7f50bd 100644 --- a/pkg/ast/scanner.go +++ b/pkg/ast/scanner.go @@ -134,14 +134,7 @@ func (s *scanner) Scan() (tok token, lit string, pos Pos) { s.next() case s.ch == '`': tok = tokCExpr - for s.next(); s.ch != '`' && s.ch != '\n'; s.next() { - } - if s.ch == '\n' { - s.Error(pos, "C expression is not terminated") - } else { - lit = string(s.data[pos.Off+1 : s.off]) - s.next() - } + lit = s.scanCExpr(pos) case s.prev2 == tokDefine && s.prev1 == tokIdent: // Note: the old form for C expressions, not really lexable. // TODO(dvyukov): get rid of this eventually. @@ -155,74 +148,16 @@ func (s *scanner) Scan() (tok token, lit string, pos Pos) { } lit = string(s.data[pos.Off+1 : s.off]) case s.ch == '"' || s.ch == '<': - // TODO(dvyukov): get rid of <...> strings, that's only includes tok = tokString - closing := byte('"') - if s.ch == '<' { - closing = '>' - } - for s.next(); s.ch != closing; s.next() { - if s.ch == 0 || s.ch == '\n' { - s.Error(pos, "string literal is not terminated") - return - } - } - lit = string(s.data[pos.Off+1 : s.off]) - for i := 0; i < len(lit); i++ { - if lit[i] < 0x20 || lit[i] >= 0x80 { - pos1 := pos - pos1.Col += i + 1 - pos1.Off += i + 1 - s.Error(pos1, "illegal character %#U in string literal", lit[i]) - break - } - } - s.next() + lit = s.scanStr(pos) case s.ch >= '0' && s.ch <= '9': tok = tokInt - for s.ch >= '0' && s.ch <= '9' || - s.ch >= 'a' && s.ch <= 'f' || - s.ch >= 'A' && s.ch <= 'F' || s.ch == 'x' { - s.next() - } - lit = string(s.data[pos.Off:s.off]) - bad := false - if _, err := strconv.ParseUint(lit, 10, 64); err != nil { - if len(lit) > 2 && lit[0] == '0' && lit[1] == 'x' { - if _, err := strconv.ParseUint(lit[2:], 16, 64); err != nil { - bad = true - } - } else { - bad = true - } - } - if bad { - s.Error(pos, fmt.Sprintf("bad integer %q", lit)) - lit = "0" - } + lit = s.scanInt(pos) case s.ch == '\'': tok = tokInt - lit = "0" - s.next() - s.next() - if s.ch != '\'' { - s.Error(pos, "char literal is not terminated") - return - } - s.next() - lit = string(s.data[pos.Off : pos.Off+3]) + lit = s.scanChar(pos) case s.ch == '_' || s.ch >= 'a' && s.ch <= 'z' || s.ch >= 'A' && s.ch <= 'Z': - tok = tokIdent - for s.ch == '_' || s.ch == '$' || - s.ch >= 'a' && s.ch <= 'z' || - s.ch >= 'A' && s.ch <= 'Z' || - s.ch >= '0' && s.ch <= '9' { - s.next() - } - lit = string(s.data[pos.Off:s.off]) - if key, ok := keywords[lit]; ok { - tok = key - } + tok, lit = s.scanIdent(pos) default: tok = punctuation[s.ch] if tok == tokIllegal { @@ -235,6 +170,94 @@ func (s *scanner) Scan() (tok token, lit string, pos Pos) { return } +func (s *scanner) scanCExpr(pos Pos) string { + for s.next(); s.ch != '`' && s.ch != '\n'; s.next() { + } + if s.ch == '\n' { + s.Error(pos, "C expression is not terminated") + return "" + } + lit := string(s.data[pos.Off+1 : s.off]) + s.next() + return lit +} + +func (s *scanner) scanStr(pos Pos) string { + // TODO(dvyukov): get rid of <...> strings, that's only includes + closing := byte('"') + if s.ch == '<' { + closing = '>' + } + for s.next(); s.ch != closing; s.next() { + if s.ch == 0 || s.ch == '\n' { + s.Error(pos, "string literal is not terminated") + return "" + } + } + lit := string(s.data[pos.Off+1 : s.off]) + for i := 0; i < len(lit); i++ { + if lit[i] < 0x20 || lit[i] >= 0x80 { + pos1 := pos + pos1.Col += i + 1 + pos1.Off += i + 1 + s.Error(pos1, "illegal character %#U in string literal", lit[i]) + break + } + } + s.next() + return lit +} + +func (s *scanner) scanInt(pos Pos) string { + for s.ch >= '0' && s.ch <= '9' || + s.ch >= 'a' && s.ch <= 'f' || + s.ch >= 'A' && s.ch <= 'F' || s.ch == 'x' { + s.next() + } + lit := string(s.data[pos.Off:s.off]) + bad := false + if _, err := strconv.ParseUint(lit, 10, 64); err != nil { + if len(lit) > 2 && lit[0] == '0' && lit[1] == 'x' { + if _, err := strconv.ParseUint(lit[2:], 16, 64); err != nil { + bad = true + } + } else { + bad = true + } + } + if bad { + s.Error(pos, fmt.Sprintf("bad integer %q", lit)) + lit = "0" + } + return lit +} + +func (s *scanner) scanChar(pos Pos) string { + s.next() + s.next() + if s.ch != '\'' { + s.Error(pos, "char literal is not terminated") + return "0" + } + s.next() + return string(s.data[pos.Off : pos.Off+3]) +} + +func (s *scanner) scanIdent(pos Pos) (tok token, lit string) { + tok = tokIdent + for s.ch == '_' || s.ch == '$' || + s.ch >= 'a' && s.ch <= 'z' || + s.ch >= 'A' && s.ch <= 'Z' || + s.ch >= '0' && s.ch <= '9' { + s.next() + } + lit = string(s.data[pos.Off:s.off]) + if key, ok := keywords[lit]; ok { + tok = key + } + return +} + func (s *scanner) Error(pos Pos, msg string, args ...interface{}) { s.errors++ s.errorHandler(pos, fmt.Sprintf(msg, args...)) diff --git a/pkg/ifuzz/decode.go b/pkg/ifuzz/decode.go index 1e657ec00..4e4c5873e 100644 --- a/pkg/ifuzz/decode.go +++ b/pkg/ifuzz/decode.go @@ -10,6 +10,7 @@ import ( // Decode decodes instruction length for the given mode. // It can have falsely decode incorrect instructions, // but should not fail to decode correct instructions. +// nolint: gocyclo func Decode(mode int, text []byte) (int, error) { if len(text) == 0 { return 0, fmt.Errorf("zero-length instruction") diff --git a/pkg/ifuzz/encode.go b/pkg/ifuzz/encode.go index 0b3732aa7..4780c4bd8 100644 --- a/pkg/ifuzz/encode.go +++ b/pkg/ifuzz/encode.go @@ -11,6 +11,7 @@ import ( "math/rand" ) +// nolint: gocyclo func (insn *Insn) Encode(cfg *Config, r *rand.Rand) []byte { if !insn.isCompatible(cfg) { panic("instruction is not suitable for this mode") diff --git a/pkg/ifuzz/gen/gen.go b/pkg/ifuzz/gen/gen.go index 499d41633..1368d2089 100644 --- a/pkg/ifuzz/gen/gen.go +++ b/pkg/ifuzz/gen/gen.go @@ -176,12 +176,14 @@ func (err errSkip) Error() string { return string(err) } +// nolint: gocyclo func parsePattern(insn *ifuzz.Insn, vals []string) error { if insn.Opcode != nil { return fmt.Errorf("PATTERN is already parsed for the instruction") } // As spelled these have incorrect format for 16-bit addressing mode and with 67 prefix. - if insn.Name == "NOP5" || insn.Name == "NOP6" || insn.Name == "NOP7" || insn.Name == "NOP8" || insn.Name == "NOP9" { + if insn.Name == "NOP5" || insn.Name == "NOP6" || insn.Name == "NOP7" || + insn.Name == "NOP8" || insn.Name == "NOP9" { return errSkip("") } if insn.Mode == 0 { @@ -270,10 +272,8 @@ func parsePattern(insn *ifuzz.Insn, vals []string) error { insn.Mod = 1 case v == "MOD=2": insn.Mod = 2 - case v == "MODRM()": case v == "lock_prefix": insn.Prefix = append(insn.Prefix, 0xF0) - case v == "nolock_prefix": // Immediates. case v == "UIMM8()", v == "SIMM8()": @@ -331,8 +331,6 @@ func parsePattern(insn *ifuzz.Insn, vals []string) error { insn.VexL = -1 case v == "VL256", v == "VL=1": insn.VexL = 1 - case v == "VL512": - // VL=2 case v == "NOVSR": insn.VexNoR = true case v == "NOEVSR": @@ -340,49 +338,6 @@ func parsePattern(insn *ifuzz.Insn, vals []string) error { // VEXDEST3=0b1 VEXDEST210=0b111 VEXDEST4=0b0 case v == "SE_IMM8()": addImm(insn, 1) - case v == "VMODRM_XMM()": - case v == "VMODRM_YMM()": - case v == "BCRC=0": - case v == "BCRC=1": - case v == "ESIZE_8_BITS()": - case v == "ESIZE_16_BITS()": - case v == "ESIZE_32_BITS()": - case v == "ESIZE_64_BITS()": - case v == "NELEM_GPR_WRITER_STORE()": - case v == "NELEM_GPR_WRITER_STORE_BYTE()": - case v == "NELEM_GPR_WRITER_STORE_WORD()": - case v == "NELEM_GPR_WRITER_LDOP_Q()": - case v == "NELEM_GPR_WRITER_LDOP_D()": - case v == "NELEM_GPR_READER()": - case v == "NELEM_GPR_READER_BYTE()": - case v == "NELEM_GPR_READER_WORD()": - case v == "NELEM_GSCAT()": - case v == "NELEM_HALF()": - case v == "NELEM_FULL()": - case v == "NELEM_FULLMEM()": - case v == "NELEM_QUARTERMEM()": - case v == "NELEM_EIGHTHMEM()": - case v == "NELEM_HALFMEM()": - case v == "NELEM_QUARTERMEM()": - case v == "NELEM_MEM128()": - case v == "NELEM_SCALAR()": - case v == "NELEM_TUPLE1()": - case v == "NELEM_TUPLE2()": - case v == "NELEM_TUPLE4()": - case v == "NELEM_TUPLE8()": - case v == "NELEM_TUPLE1_4X()": - case v == "NELEM_TUPLE1_BYTE()": - case v == "NELEM_TUPLE1_WORD()": - case v == "NELEM_MOVDDUP()": - case v == "UISA_VMODRM_XMM()": - case v == "UISA_VMODRM_YMM()": - case v == "UISA_VMODRM_ZMM()": - case v == "MASK=0": - case v == "FIX_ROUND_LEN128()": - case v == "FIX_ROUND_LEN512()": - case v == "AVX512_ROUND()": - case v == "ZEROING=0": - case v == "SAE()": // Modes case v == "mode64": @@ -393,17 +348,16 @@ func parsePattern(insn *ifuzz.Insn, vals []string) error { insn.Mode &= 1 << ifuzz.ModeProt32 case v == "mode16": insn.Mode &= 1<