   ;; we cannot use 'symbols in regexp-opt because GNU Emacs <24
   ;; doesn't understand that
   (append
-   `((,(go--regexp-enclose-in-symbol (regexp-opt go-mode-keywords t)) . font-lock-keyword-face)
+   `((go--match-func
+      ,@(mapcar (lambda (x) `(,x font-lock-type-face))
+                (number-sequence 1 go--font-lock-func-param-num-groups)))
+     (,(go--regexp-enclose-in-symbol (regexp-opt go-mode-keywords t)) . font-lock-keyword-face)
      (,(concat "\\(" (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) "\\)[[:space:]]*(") 1 font-lock-builtin-face)
      (,(go--regexp-enclose-in-symbol (regexp-opt go-constants t)) . font-lock-constant-face)
      (,go-func-regexp 1 font-lock-function-name-face)) ;; function (not method) name

(defun go--match-raw-string-literal (end)
  "Search for a raw string literal. Set point to the end of the
occurence found on success. Returns nil on failure."
  (when (search-forward "`" end t)
    (goto-char (match-beginning 0))
    (if (go-in-string-or-comment-p)
        (progn (goto-char (match-end 0))
               (go--match-raw-string-literal end))
      (when (looking-at "\\(`\\)\\([^`]*\\)\\(`\\)")
        (goto-char (match-end 0))
        t))))

(defun go-previous-line-has-dangling-op-p ()
  "Returns non-nil if the current line is a continuation line."

    (while (>= (go-paren-level) orig-level)
      (skip-chars-forward "^}")
      (forward-char))))

(defun go--find-enclosing-parentheses (position)
  "Return points of outermost '(' and ')' surrounding POSITION if
such parentheses exist.

If outermost '(' exists but ')' does not, it returns the next blank
line or end-of-buffer position instead of the position of the closing
parenthesis.

If the starting parenthesis is not found, it returns (POSITION
POSITION).
"
  (save-excursion
    (let (beg end)
      (goto-char position)
      (while (> (go-paren-level) 0)
        (re-search-backward "[(\\[{]" nil t)
        (when (looking-at "(")
          (setq beg (point))))
      (if (null beg)
          (list position position)
        (goto-char position)
        (while (and (> (go-paren-level) 0)
                    (search-forward ")" nil t)))
        (when (> (go-paren-level) 0)
          (unless (re-search-forward "^[[:space:]]*$" nil t)
            (goto-char (point-max))))
        (list beg (point))))))

(defun go--search-next-comma (end)
  "Search forward from point for a comma whose nesting level is
the same as point. If it reaches the end of line or a closing
parenthesis before a comma, it stops at it."
  (let ((orig-level (go-paren-level)))
    (while (and (< (point) end)
                (or (looking-at "[^,)\n]")
                    (> (go-paren-level) orig-level)))
      (forward-char))
    (when (and (looking-at ",")
               (< (point) (1- end)))
      (forward-char))))

(defun go--looking-at-keyword ()
  (and (looking-at (concat "\\(" go-identifier-regexp "\\)"))
       (member (match-string 1) go-mode-keywords)))

(defun go--match-func (end)
  "Search for identifiers used as type names from a function
parameter list, and set the identifier positions as the results
of last search. Return t if search succeeded."
  (when (re-search-forward (go--regexp-enclose-in-symbol "func") end t)
    (let ((regions (go--match-func-type-names end)))
      (if (null regions)
          ;; Nothing to highlight. This can happen if the current func
          ;; is "func()". Try next one.
          (go--match-func end)
        ;; There are something to highlight. Set those positions as
        ;; last search results.
        (setq regions (go--filter-match-data regions end))
        (when regions
          (set-match-data (go--make-match-data regions))
          t)))))

(defun go--match-func-type-names (end)
  (cond
   ;; Function declaration (e.g. "func foo(")
   ((looking-at (concat "[[:space:]\n]*" go-identifier-regexp "[[:space:]\n]*("))
    (goto-char (match-end 0))
    (nconc (go--match-parameter-list end)
           (go--match-function-result end)))
   ;; Method declaration, function literal, or function type
   ((looking-at "[[:space:]]*(")
    (goto-char (match-end 0))
    (let ((regions (go--match-parameter-list end)))
      ;; Method declaration (e.g. "func (x y) foo(")
      (when (looking-at (concat "[[:space:]]*" go-identifier-regexp "[[:space:]\n]*("))
        (goto-char (match-end 0))
        (setq regions (nconc regions (go--match-parameter-list end))))
      (nconc regions (go--match-function-result end))))))

(defun go--parameter-list-type (end)
  "Return 'present if the parameter list has names, or 'absent if
not, assuming point is at the beginning of a parameter list, just
after '('."
  (save-excursion
    (skip-chars-forward "[:space:]\n" end)
    (cond ((> (point) end)
           nil)
          ((looking-at (concat go-identifier-regexp "[[:space:]\n]*,"))
           (goto-char (match-end 0))
           (go--parameter-list-type end))
          ((or (looking-at go-qualified-identifier-regexp)
               (looking-at (concat go-type-name-no-prefix-regexp "[[:space:]\n]*\\(?:)\\|\\'\\)"))
               (go--looking-at-keyword)
               (looking-at "[*\\[]\\|\\.\\.\\.\\|\\'"))
           'absent)
          (t 'present))))

(defconst go--opt-dotdotdot-regexp "\\(?:\\.\\.\\.\\)?")
(defconst go--parameter-type-regexp
  (concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(" go-type-name-no-prefix-regexp "\\)[[:space:]\n]*\\([,)]\\|\\'\\)"))
(defconst go--func-type-in-parameter-list-regexp
  (concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(" (go--regexp-enclose-in-symbol "func") "\\)"))

(defun go--match-parameters-common (identifier-regexp end)
  (let ((acc ())
        (start -1))
    (while (progn (skip-chars-forward "[:space:]\n" end)
                  (and (not (looking-at "\\(?:)\\|\\'\\)"))
                       (< start (point))
                       (<= (point) end)))
      (setq start (point))
      (cond
       ((looking-at (concat identifier-regexp go--parameter-type-regexp))
        (setq acc (nconc acc (list (match-beginning 1) (match-end 1))))
        (goto-char (match-beginning 2)))
       ((looking-at (concat identifier-regexp go--func-type-in-parameter-list-regexp))
        (goto-char (match-beginning 1))
        (setq acc (nconc acc (go--match-func-type-names end)))
        (go--search-next-comma end))
       (t
        (go--search-next-comma end))))
    (when (and (looking-at ")")
               (< (point) end))
      (forward-char))
    acc))

(defun go--match-parameters-with-identifier-list (end)
  (go--match-parameters-common
   (concat go-identifier-regexp "[[:space:]\n]+")
   end))

(defun go--match-parameters-without-identifier-list (end)
  (go--match-parameters-common "" end))

(defun go--filter-match-data (regions end)
  "Remove points from regions if they are beyond end. Regions are
a list whose size is multiple of 2. Element 2n is beginning of a
region and 2n+1 is end of it.

This function is used to make sure we don't override end point
that font-lock-mode gave to us."
  (when regions
    (let* ((vec (vconcat regions))
           (i 0)
           (len (length vec)))
      (while (and (< i len)
                  (<= (nth i regions) end)
                  (<= (nth (1+ i) regions) end))
        (setq i (+ i 2)))
      (cond ((= i len)
             regions)
            ((zerop i)
             nil)
            (t
             (setcdr (nth i regions) nil)
             regions)))))

(defun go--make-match-data (regions)
  (let ((deficit (- (* 2 go--font-lock-func-param-num-groups)
                    (length regions))))
    (when (> deficit 0)
      (let ((last (car (last regions))))
        (setq regions (nconc regions (make-list deficit last))))))
  `(,(car regions) ,@(last regions) ,@regions))

(defun go--match-parameter-list (end)
  "Returns a list of identifier positions that are used as type
names in a function parameter list, assuming point is at the
beginning of a parameter list. Returns nil if the text after
point does not look like a parameter list.

Set point to end of closing parenthesis on success.

In Go, the names must either all be present or all be absent
within a list of parameters.

Parsing a parameter list is a little bit complicated because we
have to scan through the parameter list to determine whether or
not the list has names. Until a type name is found or reaching
end of a parameter list, we are not sure which form the parameter
list is.

For example, X and Y are type names in a parameter list \"(X,
Y)\" but are parameter names in \"(X, Y int)\". We cannot say if
X is a type name until we see int after Y.

Note that even \"(int, float T)\" is a valid parameter
list. Builtin type names are not reserved words. In this example,
int and float are parameter names and only T is a type name.

In this function, we first scan the parameter list to see if the
list has names, and then handle it accordingly."
  (let ((name (go--parameter-list-type end)))
    (cond ((eq name 'present)
           (go--match-parameters-with-identifier-list end))
          ((eq name 'absent)
           (go--match-parameters-without-identifier-list end))
          (t nil))))

(defun go--match-function-result (end)
  "Returns a list of identifier positions that are used as type
names in a function result, assuming point is at the beginning of
a result.

Function result is a unparenthesized type or a parameter list."
  (cond ((and (looking-at (concat "[[:space:]*]*\\(" go-type-name-no-prefix-regexp "\\)"))
              (not (member (match-string 1) go-mode-keywords)))
         (list (match-beginning 1) (match-end 1)))
        ((looking-at "[[:space:]]*(")
         (goto-char (match-end 0))
         (go--match-parameter-list end))
        (t nil)))

;;;###autoload
(define-derived-mode go-mode prog-mode "Go"
