Repository: labstack/echo
File: router.go (Go)

Code before:
		} else {
			// Not found
			break
		}
	}

	if currentNode == nil && previousBestMatchNode == nil {
		return // nothing matched at all
	}

	if matchedRouteMethod != nil {
		ctx.handler = matchedRouteMethod.handler
		ctx.path = matchedRouteMethod.ppath
		ctx.pnames = matchedRouteMethod.pnames
	} else {
		// use previous match as basis. although we have no matching handler we have path match.
		// so we can send http.StatusMethodNotAllowed (405) instead of http.StatusNotFound (404)
		currentNode = previousBestMatchNode

		ctx.path = path
		ctx.handler = NotFoundHandler
		if currentNode.isHandler {
			ctx.Set(ContextKeyHeaderAllow, currentNode.methods.allowHeader)
			ctx.handler = MethodNotAllowedHandler
			if method == http.MethodOptions {
				ctx.handler = optionsMethodHandler(currentNode.methods.allowHeader)
			}
		}
	}
}


Review comment (bug):
I do not think that moving these assignments is a good thing. Although `c.Reset` will reset context in `e.ServeHTTP` it is more foolproof to make sure that every codepath will assign something in router to these values. so doing these here is better than relying some if branch somewhere be smart enough to know all corner cases.  

Consider this testcase which will fail due after second  `r.Find`  param names still contains old values from previous request and path value is set incorrectly to request path. If you check  `context.Param()` and `context.ParamValues()` you will see that their result depends directly on `pnames` contents/size.
```go
func TestRouterDifferentParamsInPath(t *testing.T) {
	e := New()
	r := e.router
	r.Add(http.MethodPut, "/*", func(Context) error {
		return nil
	})
	r.Add(http.MethodPut, "/users/:vid/files/:gid", func(Context) error {
		return nil
	})
	r.Add(http.MethodGet, "/users/:uid/files/:fid", func(Context) error {
		return nil
	})
	c := e.NewContext(nil, nil).(*context)

	r.Find(http.MethodGet, "/users/1/files/2", c)
	assert.Equal(t, c.ParamNames(), []string{"uid", "fid"})
	assert.Equal(t, "1", c.Param("uid"))
	assert.Equal(t, "2", c.Param("fid"))

	r.Find(http.MethodGet, "/users/1/shouldBacktrackToFirstAnyRouteAnd405", c)
	assert.Equal(t, "/*", c.Path()) // we should have (partially by 405) matched route with path `/*` by backtracking logic

	r.Find(http.MethodPut, "/users/3/files/4", c)
	assert.Equal(t, c.ParamNames(), []string{"vid", "gid"})
	assert.Equal(t, "3", c.Param("vid"))
	assert.Equal(t, "4", c.Param("gid"))
}
```

Code after:
		// Let's backtrack to the first possible alternative node of the decision path
		nk, ok := backtrackToNextNodeKind(anyKind)
		if !ok {
			break // No other possibilities on the decision path
		} else if nk == paramKind {
			goto Param
		} else if nk == anyKind {
			goto Any
		} else {
			// Not found
			break
		}
	}

	if currentNode == nil && previousBestMatchNode == nil {
		return // nothing matched at all
	}

	var rPath string
	var rPNames []string
	if matchedRouteMethod != nil {
		ctx.handler = matchedRouteMethod.handler
		rPath = matchedRouteMethod.ppath
		rPNames = matchedRouteMethod.pnames
	} else {
		// use previous match as basis. although we have no matching handler we have path match.
		// so we can send http.StatusMethodNotAllowed (405) instead of http.StatusNotFound (404)
		currentNode = previousBestMatchNode

		rPath = currentNode.originalPath
		rPNames = nil // no params here
		ctx.handler = NotFoundHandler
		if currentNode.isHandler {
			ctx.Set(ContextKeyHeaderAllow, currentNode.methods.allowHeader)
			ctx.handler = MethodNotAllowedHandler
			if method == http.MethodOptions {
				ctx.handler = optionsMethodHandler(currentNode.methods.allowHeader)
			}
		}
	}
	ctx.path = rPath
	ctx.pnames = rPNames
}