Repository: Konstantin8105/c4go
Pull request #351 (2019-03-21 12:32:34)

redesign cases

codecov[bot]: # [Codecov](https://codecov.io/gh/Konstantin8105/c4go/pull/351?src=pr&el=h1) Report
> Merging [#351](https://codecov.io/gh/Konstantin8105/c4go/pull/351?src=pr&el=desc) into [master](https://codecov.io/gh/Konstantin8105/c4go/commit/b849228ab41d83b23c7f95756a4167042be85624?src=pr&el=desc) will **decrease** coverage by `0.14%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/Konstantin8105/c4go/pull/351/graphs/tree.svg?width=650&token=esH2rHXlZ2&height=150&src=pr)](https://codecov.io/gh/Konstantin8105/c4go/pull/351?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #351      +/-   ##
==========================================
- Coverage   86.45%   86.31%   -0.15%     
==========================================
  Files         186      186              
  Lines       10639    10614      -25     
==========================================
- Hits         9198     9161      -37     
- Misses       1027     1035       +8     
- Partials      414      418       +4
```


| [Impacted Files](https://codecov.io/gh/Konstantin8105/c4go/pull/351?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [transpiler/variables.go](https://codecov.io/gh/Konstantin8105/c4go/pull/351/diff?src=pr&el=tree#diff-dHJhbnNwaWxlci92YXJpYWJsZXMuZ28=) | `84.21% <100%> (ø)` | :arrow_up: |
| [transpiler/switch.go](https://codecov.io/gh/Konstantin8105/c4go/pull/351/diff?src=pr&el=tree#diff-dHJhbnNwaWxlci9zd2l0Y2guZ28=) | `82.27% <100%> (-7.89%)` | :arrow_down: |
| [transpiler/util.go](https://codecov.io/gh/Konstantin8105/c4go/pull/351/diff?src=pr&el=tree#diff-dHJhbnNwaWxlci91dGlsLmdv) | `88.23% <0%> (-5.89%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Konstantin8105/c4go/pull/351?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Konstantin8105/c4go/pull/351?src=pr&el=footer). Last update [b849228...9fab218](https://codecov.io/gh/Konstantin8105/c4go/pull/351?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/scripts/sqlite.sh b/scripts/sqlite.sh
index 2ab80f6..a106e9e 100755
--- a/scripts/sqlite.sh
+++ b/scripts/sqlite.sh
@@ -43,8 +43,18 @@ SQLITE_WARNINGS=`cat $SQLITE_TEMP_FOLDER/sqlite.go | grep "^// Warning" | sort |
 echo "After transpiling shell.c and sqlite3.c together, have summary: $SQLITE_WARNINGS warnings."
 
 # Show amount error from `go build`:
-SQLITE_WARNINGS_GO=`go build $SQLITE_TEMP_FOLDER/sqlite.go -gcflags="-e" 2>&1 | wc -l`
+SQLITE_WARNINGS_GO=`go build -o $SQLITE_TEMP_FOLDER/sqlite.app $SQLITE_TEMP_FOLDER/sqlite.go -gcflags="-e" 2>&1 | wc -l`
 echo "In file sqlite.go summary : $SQLITE_WARNINGS_GO warnings in go build."
 
 SQLITE_UNSAFE=`cat $SQLITE_TEMP_FOLDER/sqlite.go | grep "unsafe\." | wc -l`
 echo "Amount unsafe package using: $SQLITE_UNSAFE"
+
+# Arguments menu
+echo "    -s for show detail of Go build errors"
+if [ "$1" == "-s" ]; then
+	# show go build warnings	
+		# c4go warnings
+			cat $SQLITE_TEMP_FOLDER/sqlite.go | grep "^// Warning" | sort | uniq
+		# show amount error from `go build`:
+			go build -o $SQLITE_TEMP_FOLDER/sqlite.app -gcflags="-e"  $SQLITE_TEMP_FOLDER/sqlite.go 2>&1
+fi
diff --git a/transpiler/switch.go b/transpiler/switch.go
index 69abe57..7c14057 100644
--- a/transpiler/switch.go
+++ b/transpiler/switch.go
@@ -13,6 +13,176 @@ import (
 	"github.com/Konstantin8105/c4go/types"
 )
 
+// From :
+// CaseStmt
+// |-UnaryOperator 'int' prefix '-'
+// | `-IntegerLiteral 'int' 1
+// |-<<<NULL>>>
+// `-CaseStmt
+//   |-BinaryOperator 'int' '-'
+//   | |- ...
+//   |-<<<NULL>>>
+//   `-CaseStmt
+//     |- ...
+//     |-<<<NULL>>>
+//     `-DefaultStmt
+//       `- ...
+// To:
+// CaseStmt
+// |-UnaryOperator 'int' prefix '-'
+// | `-IntegerLiteral 'int' 1
+// `-<<<NULL>>>
+// <<<NULL>>>
+// CaseStmt
+// |-BinaryOperator 'int' '-'
+// | |- ...
+// `-<<<NULL>>>
+// <<<NULL>>>
+// CaseStmt
+// |- ...
+// |-<<<NULL>>>
+// `-DefaultStmt
+//   `- ...
+// <<<NULL>>>
+//
+// From:
+// |-CaseStmt
+// | `- ...
+// |-NullStmt
+// |-BreakStmt 0
+// |-CaseStmt
+// | `-...
+// To:
+// CaseStmt
+// `- ...
+// <<<NULL>>>
+// NullStmt
+// <<<NULL>>>
+// BreakStmt 0
+// <<<NULL>>>
+// CaseStmt
+// `-...
+// <<<NULL>>>
+//
+// From:
+// |-CaseStmt
+// | `- ...
+// |-CompoundAssignOperator  'int' '+=' ComputeLHSTy='int' ComputeResultTy='int'
+// | `-...
+// |-BreakStmt
+// |-CaseStmt
+// | |- ...
+// To:
+// `-CaseStmt
+//   `- ...
+// <<<NULL>>>
+// CompoundAssignOperator  'int' '+=' ComputeLHSTy='int' ComputeResultTy='int'
+// `-...
+// <<<NULL>>>
+// BreakStmt
+// <<<NULL>>>
+// CaseStmt
+// `- ...
+// <<<NULL>>>
+//
+func caseSplitter(nodes ...ast.Node) (cs []ast.Node) {
+
+	if nodes == nil {
+		return
+	}
+	if len(nodes) == 0 {
+		return
+	}
+	if len(nodes) > 1 {
+		for i := range nodes {
+			cs = append(cs, caseSplitter(nodes[i])...)
+		}
+		return
+	}
+
+	node := nodes[0]
+	if node == nil {
+		return
+	}
+
+	var compountWithCase func(ast.Node) bool
+	compountWithCase = func(node ast.Node) bool {
+		if node == nil {
+			return false
+		}
+
+		switch node.(type) {
+		case *ast.CaseStmt, *ast.DefaultStmt:
+			return true
+		}
+
+		for i, n := range node.Children() {
+			if _, ok := n.(*ast.SwitchStmt); ok {
+				continue
+			}
+			if compountWithCase(node.Children()[i]) {
+				return true
+			}
+		}
+
+		return false
+	}
+
+	var ns []ast.Node
+	switch node.(type) {
+	case *ast.CompoundStmt:
+		if compountWithCase(node) {
+			ns = node.(*ast.CompoundStmt).ChildNodes
+			node.(*ast.CompoundStmt).ChildNodes = nil
+		}
+
+	case *ast.CaseStmt:
+		ns = node.(*ast.CaseStmt).ChildNodes
+		node.(*ast.CaseStmt).ChildNodes = nil
+
+	case *ast.DefaultStmt:
+		ns = node.(*ast.DefaultStmt).ChildNodes
+		node.(*ast.DefaultStmt).ChildNodes = nil
+	}
+
+	cs = append(cs, node)
+	cs = append(cs, caseSplitter(ns...)...)
+
+	return
+}
+
+func caseMerge(nodes []ast.Node) (pre, cs []ast.Node) {
+	for i := range nodes {
+		// ignore empty *ast.CompountStmt
+		if comp, ok := nodes[i].(*ast.CompoundStmt); ok && len(comp.Children()) == 0 {
+			continue
+		}
+
+		var isCaseType bool
+
+		if _, ok := nodes[i].(*ast.CaseStmt); ok {
+			isCaseType = true
+		}
+		if _, ok := nodes[i].(*ast.DefaultStmt); ok {
+			isCaseType = true
+		}
+
+		if isCaseType {
+			cs = append(cs, nodes[i])
+			continue
+		}
+
+		if len(cs) == 0 {
+			pre = append(pre, nodes[i])
+			continue
+		}
+
+		cs[len(cs)-1].AddChild(nodes[i])
+	}
+
+	return
+}
+
 func transpileSwitchStmt(n *ast.SwitchStmt, p *program.Program) (
 	_ *goast.SwitchStmt, preStmts []goast.Stmt, postStmts []goast.Stmt, err error) {
 	defer func() {
@@ -52,98 +222,37 @@ func transpileSwitchStmt(n *ast.SwitchStmt, p *program.Program) (
 	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)
 
 	// separation body of switch on cases
-	body := n.Children()[len(n.Children())-1].(*ast.CompoundStmt)
-
-	// solving switch case without body
-	// case -1:
-	// default: ...
-checkAgain:
-	for i := range body.Children() {
-		found := false
-		if _, ok := body.Children()[i].(*ast.CaseStmt); ok {
-			found = true
-		}
-		if _, ok := body.Children()[i].(*ast.DefaultStmt); ok {
-			found = true
-		}
-
-		if !found {
-			continue
-		}
-
-		v := body.Children()[i]
-		if vv, ok := v.Children()[len(v.Children())-1].(*ast.CaseStmt); ok {
-			if len(body.Children()) > i+1 {
-				body.ChildNodes = append(body.ChildNodes[:i+1], append([]ast.Node{vv}, body.ChildNodes[i+1:]...)...)
-			} else {
-				up := body.ChildNodes
-				body.ChildNodes = append(up, vv)
-			}
-			v.Children()[len(v.Children())-1] = &ast.CompoundStmt{}
-			goto checkAgain
-		}
-		if vv, ok := v.Children()[len(v.Children())-1].(*ast.DefaultStmt); ok {
-			if len(body.Children()) > i+1 {
-				body.ChildNodes = append(body.ChildNodes[:i+1], append([]ast.Node{vv}, body.ChildNodes[i+1:]...)...)
-			} else {
-				up := body.ChildNodes
-				body.ChildNodes = append(up, vv)
-			}
-			v.Children()[len(v.Children())-1] = &ast.CompoundStmt{}
-			goto checkAgain
-		}
+	body, ok := n.Children()[len(n.Children())-1].(*ast.CompoundStmt)
+	if !ok {
+		err = fmt.Errorf("body is not ast.CompoundStmt : %T", n.Children()[len(n.Children())-1])
 	}
 
-	for i := range body.Children() {
-		// For simplification - each CaseStmt will have CompoundStmt
-		if v, ok := body.Children()[i].(*ast.CaseStmt); ok {
-			if _, ok := v.Children()[len(v.Children())-1].(*ast.CompoundStmt); !ok {
-				var compoundStmt ast.CompoundStmt
-				compoundStmt.AddChild(v.Children()[len(v.Children())-1])
-				v.Children()[len(v.Children())-1] = &compoundStmt
-			}
-		}
-		// For simplification - each DefaultStmt will have CompoundStmt
-		if v, ok := body.Children()[i].(*ast.DefaultStmt); ok {
-			if _, ok := v.Children()[len(v.Children())-1].(*ast.CompoundStmt); !ok {
-				var compoundStmt ast.CompoundStmt
-				compoundStmt.AddChild(v.Children()[len(v.Children())-1])
-				v.Children()[len(v.Children())-1] = &compoundStmt
-			}
-		}
-	}
-
-	// Move element inside CompoundStmt
-	for i := 0; i < len(body.Children()); i++ {
-		switch body.Children()[i].(type) {
-		case *ast.CaseStmt, *ast.DefaultStmt:
-			// do nothing
-		default:
-			if i != 0 {
-				lastStmt := body.Children()[i-1].Children()
-				if comp, ok := lastStmt[len(lastStmt)-1].(*ast.CompoundStmt); ok {
-					// add node in CompoundStmt
-					comp.AddChild(body.Children()[i])
-
-					// remove from body
-					if i+1 < len(body.Children()) {
-						body.ChildNodes = append(body.ChildNodes[:i], body.ChildNodes[i+1:]...)
-					} else {
-						body.ChildNodes = body.ChildNodes[:i]
-					}
-
-					// goto to last iteration
-					i--
-				} else {
-					p.AddMessage(p.GenerateWarningMessage(
-						fmt.Errorf("Unexpected element"), n))
-				}
-			} else {
-				p.AddMessage(p.GenerateWarningMessage(
-					fmt.Errorf("Unsupport case"), n))
-			}
-
-		}
+	// CompoundStmt
+	// `-CaseStmt
+	//   |-UnaryOperator 'int' prefix '-'
+	//   | `-IntegerLiteral 'int' 1
+	//   |-<<<NULL>>>
+	//   `-CaseStmt
+	//     |-BinaryOperator 'int' '-'
+	//     | |- ...
+	//     |-<<<NULL>>>
+	//     `-CaseStmt
+	//       |- ...
+	//       |-<<<NULL>>>
+	//       `-DefaultStmt
+	//         `- ...
+	parts := caseSplitter(body.Children()...)
+	pre, parts := caseMerge(parts)
+	body.ChildNodes = parts
+
+	if len(pre) > 0 {
+		stmt, newPre, newPost, err := transpileCompoundStmt(&ast.CompoundStmt{
+			ChildNodes: pre,
+		}, p)
+		p.AddMessage(p.GenerateWarningMessage(err, n))
+		preStmts = append(preStmts, newPre...)
+		preStmts = append(preStmts, stmt.List...)
+		preStmts = append(preStmts, newPost...)
 	}
 
 	// The body will always be a CompoundStmt because a switch statement is not
@@ -155,52 +264,126 @@ checkAgain:
 
 	preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)
 
-	// For simplification switch case:
+	// TODO
+	//
 	// from:
+	// case 2:
+	//  {
+	//  }
+	//  fallthrough
 	// case 3:
-	// 	{
-	// 		var c int
-	// 		return
-	// 	}
-	// 	fallthrough
+	//  fallthrough
+	// case 4:
+	//  break
 	// to:
-	// case 3:
-	// 	var c int
-	// 	return
+	// case 2,3:
+	//  fallthrough
+	// case 4:
+	//  break
 	//
+	// from:
+	// 		fallthrough
+	//		break
+	// to:
+	//		---
+
+	// cases with 2 nodes
+	// from :
+	//		{
+	//			...
+	//		}
+	//		break or fallthrough
+	// to:
+	//		...
+	//		break or fallthrough
 	for i := range cases {
 		body := cases[i].Body
 		if len(body) != 2 {
 			continue
 		}
-		var isFallThrough bool
-		if v, ok := body[1].(*goast.BranchStmt); ok {
-			isFallThrough = (v.Tok == token.FALLTHROUGH)
+		var (
+			last    = body[len(body)-1]
+			prelast = body[len(body)-2]
+		)
+		br, ok := last.(*goast.BranchStmt)
+		if !ok || !(br.Tok == token.FALLTHROUGH || br.Tok == token.BREAK) {
+			continue
 		}
-		if !isFallThrough {
-			if len(body) > 1 {
-				cases[i].Body = body
-			}
+		bl, ok := prelast.(*goast.BlockStmt)
+		if !ok {
 			continue
 		}
-		if v, ok := body[0].(*goast.BlockStmt); ok {
-			if len(v.List) > 0 {
-				if vv, ok := v.List[len(v.List)-1].(*goast.BranchStmt); ok {
-					if vv.Tok == token.BREAK {
-						if isFallThrough {
-							cases[i].Body = append(v.List[:len(v.List)-1])
-							continue
-						}
-					}
-				}
-				if _, ok := v.List[len(v.List)-1].(*goast.ReturnStmt); ok {
-					cases[i].Body = body[:len(body)-1]
-					continue
-				}
-			} else {
-				cases[i].Body = []goast.Stmt{body[1]}
-			}
+
+		cases[i].Body = append(bl.List, br)
+	}
+
+	// from:
+	//		return
+	//		fallthrough
+	// to:
+	//		return
+	for i := range cases {
+		body := cases[i].Body
+		if len(body) < 2 {
+			continue
+		}
+		var (
+			last    = body[len(body)-1]
+			prelast = body[len(body)-2]
+		)
+		if br, ok := last.(*goast.BranchStmt); !ok || br.Tok != token.FALLTHROUGH {
+			continue
+		}
+		if _, ok := prelast.(*goast.ReturnStmt); !ok {
+			continue
+		}
+		cases[i].Body = cases[i].Body[:len(cases[i].Body)-1]
+	}
+
+	// from:
+	//		break
+	// 		fallthrough
+	// to:
+	//		---
+	for i := range cases {
+		body := cases[i].Body
+		if len(body) < 2 {
+			continue
 		}
+		var (
+			last    = body[len(body)-1]
+			prelast = body[len(body)-2]
+		)
+		if br, ok := last.(*goast.BranchStmt); !ok || br.Tok != token.FALLTHROUGH {
+			continue
+		}
+		if br, ok := prelast.(*goast.BranchStmt); !ok || br.Tok != token.BREAK {
+			continue
+		}
+		cases[i].Body = cases[i].Body[:len(cases[i].Body)-2]
+	}
+
+	// cases with 1 node
+	// from :
+	//		{
+	//			...
+	//		}
+	// to:
+	//		...
+	for i := range cases {
+		body := cases[i].Body
+		if len(body) != 1 {
+			continue
+		}
+		var (
+			last = body[len(body)-1]
+		)
+		bl, ok := last.(*goast.BlockStmt)
+		if !ok {
+			continue
+		}
+
+		cases[i].Body = bl.List
 	}
 
 	// Convert the normalized cases back into statements so they can be children
diff --git a/transpiler/variables.go b/transpiler/variables.go
index 2b26306..3dc09c4 100644
--- a/transpiler/variables.go
+++ b/transpiler/variables.go
@@ -37,7 +37,7 @@ func transpileDeclRefExpr(n *ast.DeclRefExpr, p *program.Program) (
 		p.AddMessage(p.GenerateWarningMessage(err, n))
 		if includeFile != "" && p.IncludeHeaderIsExists(includeFile) {
 			name := p.GetFunctionDefinition(n.Name).Substitution
-			if strings.Contains(name, ".") {
+			if strings.Contains(name, ".") && !strings.Contains(name, "github") {
 				p.AddImport(strings.Split(name, ".")[0])
 			}
 			return goast.NewIdent(name), n.Type, nil


Test patch:
diff --git a/tests/code_quality/switch.go.expected b/tests/code_quality/switch.go.expected
index fd5b3c1..15a978e 100644
--- a/tests/code_quality/switch.go.expected
+++ b/tests/code_quality/switch.go.expected
@@ -14,27 +14,19 @@ func switch_function() {
 	case (0):
 		fallthrough
 	case (1):
-		{
-			return
-		}
+		return
 	case (2):
-		{
-			_ = (i)
-			return
-		}
+		_ = (i)
+		return
 	case 3:
-		{
-			var c int32
-			return
-		}
+		var c int32
+		return
 	case 4:
 	case 5:
 	case 6:
 		fallthrough
 	case 7:
-		{
-			var d int32
-			break
-		}
+		var d int32
+		break
 	}
 }
diff --git a/tests/switch.c b/tests/switch.c
index 94b0386..d996c1f 100644
--- a/tests/switch.c
+++ b/tests/switch.c
@@ -364,9 +364,28 @@ void switch_stat()
     }
 }
 
+
+int case_inside_block(int a)
+{
+	switch (a) {
+	case 1: 
+		break;
+	case 2: 
+		{
+			a = 42;
+			case 3:
+				a += 45;
+				break;
+		}
+	case 4:
+		break;
+	}
+	return a;
+}
+
 int main()
 {
-    plan(30);
+    plan(36);
 
     switch_char();
     switch_bool();
@@ -391,5 +410,12 @@ int main()
     switch_without_input();
     switch_stat();
 
+	is_eq(case_inside_block(0),0);
+	is_eq(case_inside_block(1),1);
+	is_eq(case_inside_block(2),45+42);
+	is_eq(case_inside_block(3),3+45);
+	is_eq(case_inside_block(4),4);
+	is_eq(case_inside_block(5),5);
+
     done_testing();
 }