x/ref: Add vdl "file doc", used to insert the copyright header.

This fixes issue:
https://github.com/veyron/release-issues/issues/1599

Before this change, the vdl tool would automatically include the
vanadium copyright header in all generated files.  This means
that users would also get the copyright header in their generated
files, which is wrong.

After this change, we introduce the notion of "file doc" in each
*.vdl file, which is the comment block that starts on line 1 of
the file, as long as it isn't the package doc.  We require that
all files in a vdl package have the same file doc, otherwise we
return a compilation error.  This makes it easier for java and
javascript, which generate files that don't match 1-to-1 with the
source *.vdl files.

The code generators have been updated to use this file doc when
generating their respective files.

Change-Id: Id53e1a37012d6ff29718753aa0ceb947c9ac43d8
diff --git a/lib/vdl/codegen/golang/gen.go b/lib/vdl/codegen/golang/gen.go
index 73a32eb..e7b3ff5 100644
--- a/lib/vdl/codegen/golang/gen.go
+++ b/lib/vdl/codegen/golang/gen.go
@@ -405,9 +405,7 @@
 const genGo = `
 {{$data := .}}
 {{$file := $data.File}}
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+{{$file.Package.FileDoc}}
 
 // This file was auto-generated by the vanadium vdl tool.
 // Source: {{$file.BaseName}}
diff --git a/lib/vdl/codegen/java/const.go b/lib/vdl/codegen/java/const.go
index 3ffbaec..f572908 100644
--- a/lib/vdl/codegen/java/const.go
+++ b/lib/vdl/codegen/java/const.go
@@ -4,10 +4,9 @@
 
 package java
 
-const header = `
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
+// The data passed into every template must include a FileDoc field, which
+// contains the comment for each generated file; e.g. the boilerplate copyright
+// header.
+const header = `{{.FileDoc}}
 // This file was auto-generated by the vanadium vdl tool.
 `
diff --git a/lib/vdl/codegen/java/file_array.go b/lib/vdl/codegen/java/file_array.go
index 35e3330..4812cd1 100644
--- a/lib/vdl/codegen/java/file_array.go
+++ b/lib/vdl/codegen/java/file_array.go
@@ -62,6 +62,7 @@
 	elems := strings.TrimSuffix(strings.Repeat(javaZeroValue(tdef.Type.Elem(), env)+", ", tdef.Type.Len()), ", ")
 	zeroValue := fmt.Sprintf("new %s[] {%s}", elemType, elems)
 	data := struct {
+		FileDoc           string
 		AccessModifier    string
 		Doc               string
 		ElemType          string
@@ -75,6 +76,7 @@
 		VdlTypeString     string
 		ZeroValue         string
 	}{
+		FileDoc:           tdef.File.Package.FileDoc,
 		AccessModifier:    accessModifierForName(tdef.Name),
 		Doc:               javaDocInComment(tdef.Doc),
 		ElemType:          elemType,
diff --git a/lib/vdl/codegen/java/file_client_factory.go b/lib/vdl/codegen/java/file_client_factory.go
index ecb90e2..626c5e0 100644
--- a/lib/vdl/codegen/java/file_client_factory.go
+++ b/lib/vdl/codegen/java/file_client_factory.go
@@ -37,12 +37,14 @@
 func genJavaClientFactoryFile(iface *compile.Interface, env *compile.Env) JavaFileInfo {
 	javaServiceName := vdlutil.FirstRuneToUpper(iface.Name)
 	data := struct {
+		FileDoc        string
 		AccessModifier string
 		Sources        string
 		ServiceName    string
 		PackagePath    string
 		StubName       string
 	}{
+		FileDoc:        iface.File.Package.FileDoc,
 		AccessModifier: accessModifierForName(iface.Name),
 		Sources:        iface.File.BaseName,
 		ServiceName:    javaServiceName,
diff --git a/lib/vdl/codegen/java/file_client_interface.go b/lib/vdl/codegen/java/file_client_interface.go
index e504708..c71f60e 100644
--- a/lib/vdl/codegen/java/file_client_interface.go
+++ b/lib/vdl/codegen/java/file_client_interface.go
@@ -100,6 +100,7 @@
 		methods[i] = processClientInterfaceMethod(iface, method, env)
 	}
 	data := struct {
+		FileDoc        string
 		AccessModifier string
 		Extends        string
 		Methods        []clientInterfaceMethod
@@ -108,6 +109,7 @@
 		ServiceName    string
 		Source         string
 	}{
+		FileDoc:        iface.File.Package.FileDoc,
 		AccessModifier: accessModifierForName(iface.Name),
 		Extends:        javaClientExtendsStr(iface.Embeds),
 		Methods:        methods,
diff --git a/lib/vdl/codegen/java/file_client_stub.go b/lib/vdl/codegen/java/file_client_stub.go
index 9e3b6c4..a7275a2 100644
--- a/lib/vdl/codegen/java/file_client_stub.go
+++ b/lib/vdl/codegen/java/file_client_stub.go
@@ -252,6 +252,7 @@
 	}
 	javaServiceName := vdlutil.FirstRuneToUpper(iface.Name)
 	data := struct {
+		FileDoc          string
 		AccessModifier   string
 		EmbedMethods     []clientStubEmbedMethod
 		Embeds           []clientStubEmbed
@@ -262,6 +263,7 @@
 		Source           string
 		VDLIfacePathName string
 	}{
+		FileDoc:          iface.File.Package.FileDoc,
 		AccessModifier:   accessModifierForName(iface.Name),
 		EmbedMethods:     embedMethods,
 		Embeds:           embeds,
diff --git a/lib/vdl/codegen/java/file_complex.go b/lib/vdl/codegen/java/file_complex.go
index 2f9a5a6..7d4e382 100644
--- a/lib/vdl/codegen/java/file_complex.go
+++ b/lib/vdl/codegen/java/file_complex.go
@@ -55,6 +55,7 @@
 	}
 	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
 	data := struct {
+		FileDoc        string
 		AccessModifier string
 		Doc            string
 		Name           string
@@ -65,6 +66,7 @@
 		VdlTypeName    string
 		VdlTypeString  string
 	}{
+		FileDoc:        tdef.File.Package.FileDoc,
 		AccessModifier: accessModifierForName(tdef.Name),
 		Doc:            javaDocInComment(tdef.Doc),
 		Name:           javaTypeName,
diff --git a/lib/vdl/codegen/java/file_constants.go b/lib/vdl/codegen/java/file_constants.go
index a4ba956..2ea6a90 100644
--- a/lib/vdl/codegen/java/file_constants.go
+++ b/lib/vdl/codegen/java/file_constants.go
@@ -76,11 +76,13 @@
 	}
 
 	data := struct {
+		FileDoc     string
 		ClassName   string
 		Source      string
 		PackagePath string
 		Files       []constFile
 	}{
+		FileDoc:     pkg.FileDoc,
 		ClassName:   className,
 		Source:      javaFileNames(pkg.Files),
 		PackagePath: javaPath(javaGenPkgPath(pkg.GenPath)),
diff --git a/lib/vdl/codegen/java/file_enum.go b/lib/vdl/codegen/java/file_enum.go
index af2c036..50c9752 100644
--- a/lib/vdl/codegen/java/file_enum.go
+++ b/lib/vdl/codegen/java/file_enum.go
@@ -58,6 +58,7 @@
 	}
 	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
 	data := struct {
+		FileDoc        string
 		AccessModifier string
 		EnumLabels     []string
 		Doc            string
@@ -67,6 +68,7 @@
 		VdlTypeName    string
 		VdlTypeString  string
 	}{
+		FileDoc:        tdef.File.Package.FileDoc,
 		AccessModifier: accessModifierForName(tdef.Name),
 		EnumLabels:     labels,
 		Doc:            javaDocInComment(tdef.Doc),
diff --git a/lib/vdl/codegen/java/file_errors.go b/lib/vdl/codegen/java/file_errors.go
index b140d44..cd43dff 100644
--- a/lib/vdl/codegen/java/file_errors.go
+++ b/lib/vdl/codegen/java/file_errors.go
@@ -124,11 +124,13 @@
 	}
 
 	data := struct {
+		FileDoc     string
 		ClassName   string
 		Source      string
 		PackagePath string
 		Files       []errorFile
 	}{
+		FileDoc:     pkg.FileDoc,
 		ClassName:   className,
 		Source:      javaFileNames(pkg.Files),
 		PackagePath: javaPath(javaGenPkgPath(pkg.GenPath)),
diff --git a/lib/vdl/codegen/java/file_list.go b/lib/vdl/codegen/java/file_list.go
index 79c409c..06d4c54 100644
--- a/lib/vdl/codegen/java/file_list.go
+++ b/lib/vdl/codegen/java/file_list.go
@@ -40,6 +40,7 @@
 func genJavaListFile(tdef *compile.TypeDef, env *compile.Env) JavaFileInfo {
 	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
 	data := struct {
+		FileDoc        string
 		AccessModifier string
 		Doc            string
 		ElemType       string
@@ -49,6 +50,7 @@
 		VdlTypeName    string
 		VdlTypeString  string
 	}{
+		FileDoc:        tdef.File.Package.FileDoc,
 		AccessModifier: accessModifierForName(tdef.Name),
 		Doc:            javaDocInComment(tdef.Doc),
 		ElemType:       javaType(tdef.Type.Elem(), true, env),
diff --git a/lib/vdl/codegen/java/file_map.go b/lib/vdl/codegen/java/file_map.go
index bb506f8..073cebb 100644
--- a/lib/vdl/codegen/java/file_map.go
+++ b/lib/vdl/codegen/java/file_map.go
@@ -41,6 +41,7 @@
 func genJavaMapFile(tdef *compile.TypeDef, env *compile.Env) JavaFileInfo {
 	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
 	data := struct {
+		FileDoc        string
 		AccessModifier string
 		Doc            string
 		ElemType       string
@@ -51,6 +52,7 @@
 		VdlTypeName    string
 		VdlTypeString  string
 	}{
+		FileDoc:        tdef.File.Package.FileDoc,
 		AccessModifier: accessModifierForName(tdef.Name),
 		Doc:            javaDocInComment(tdef.Doc),
 		ElemType:       javaType(tdef.Type.Elem(), true, env),
diff --git a/lib/vdl/codegen/java/file_package_info.go b/lib/vdl/codegen/java/file_package_info.go
index ac8c253..2e94276 100644
--- a/lib/vdl/codegen/java/file_package_info.go
+++ b/lib/vdl/codegen/java/file_package_info.go
@@ -31,10 +31,12 @@
 			generated = true
 
 			data := struct {
+				FileDoc     string
 				Source      string
 				PackagePath string
 				Doc         string
 			}{
+				FileDoc:     pkg.FileDoc,
 				Source:      javaFileNames(pkg.Files),
 				PackagePath: javaPath(javaGenPkgPath(pkg.GenPath)),
 				Doc:         javaDoc(file.PackageDef.Doc),
diff --git a/lib/vdl/codegen/java/file_primitive.go b/lib/vdl/codegen/java/file_primitive.go
index 295beef..cc167a6 100644
--- a/lib/vdl/codegen/java/file_primitive.go
+++ b/lib/vdl/codegen/java/file_primitive.go
@@ -73,6 +73,7 @@
 func genJavaPrimitiveFile(tdef *compile.TypeDef, env *compile.Env) JavaFileInfo {
 	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
 	data := struct {
+		FileDoc                  string
 		AccessModifier           string
 		Doc                      string
 		Name                     string
@@ -84,6 +85,7 @@
 		VdlTypeName              string
 		VdlTypeString            string
 	}{
+		FileDoc:                  tdef.File.Package.FileDoc,
 		AccessModifier:           accessModifierForName(tdef.Name),
 		Doc:                      javaDocInComment(tdef.Doc),
 		Name:                     javaTypeName,
diff --git a/lib/vdl/codegen/java/file_server_interface.go b/lib/vdl/codegen/java/file_server_interface.go
index 6a9d3fc..04b9546 100644
--- a/lib/vdl/codegen/java/file_server_interface.go
+++ b/lib/vdl/codegen/java/file_server_interface.go
@@ -73,6 +73,7 @@
 	}
 	javaServiceName := vdlutil.FirstRuneToUpper(iface.Name)
 	data := struct {
+		FileDoc           string
 		AccessModifier    string
 		Extends           string
 		Methods           []serverInterfaceMethod
@@ -83,6 +84,7 @@
 		ServerWrapperPath string
 		Source            string
 	}{
+		FileDoc:           iface.File.Package.FileDoc,
 		AccessModifier:    accessModifierForName(iface.Name),
 		Extends:           javaServerExtendsStr(iface.Embeds),
 		Methods:           methods,
diff --git a/lib/vdl/codegen/java/file_server_wrapper.go b/lib/vdl/codegen/java/file_server_wrapper.go
index e461085..401a864 100644
--- a/lib/vdl/codegen/java/file_server_wrapper.go
+++ b/lib/vdl/codegen/java/file_server_wrapper.go
@@ -247,6 +247,7 @@
 	}
 	javaServiceName := vdlutil.FirstRuneToUpper(iface.Name)
 	data := struct {
+		FileDoc         string
 		AccessModifier  string
 		EmbedMethods    []serverWrapperEmbedMethod
 		Embeds          []serverWrapperEmbed
@@ -258,6 +259,7 @@
 		Source          string
 		Doc             string
 	}{
+		FileDoc:         iface.File.Package.FileDoc,
 		AccessModifier:  accessModifierForName(iface.Name),
 		EmbedMethods:    embedMethods,
 		Embeds:          embeds,
diff --git a/lib/vdl/codegen/java/file_set.go b/lib/vdl/codegen/java/file_set.go
index 08e9c95..3547163 100644
--- a/lib/vdl/codegen/java/file_set.go
+++ b/lib/vdl/codegen/java/file_set.go
@@ -41,6 +41,7 @@
 func genJavaSetFile(tdef *compile.TypeDef, env *compile.Env) JavaFileInfo {
 	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
 	data := struct {
+		FileDoc        string
 		AccessModifier string
 		Doc            string
 		KeyType        string
@@ -50,6 +51,7 @@
 		VdlTypeName    string
 		VdlTypeString  string
 	}{
+		FileDoc:        tdef.File.Package.FileDoc,
 		AccessModifier: accessModifierForName(tdef.Name),
 		Doc:            javaDocInComment(tdef.Doc),
 		KeyType:        javaType(tdef.Type.Key(), true, env),
diff --git a/lib/vdl/codegen/java/file_struct.go b/lib/vdl/codegen/java/file_struct.go
index 77a974f..e2083e2 100644
--- a/lib/vdl/codegen/java/file_struct.go
+++ b/lib/vdl/codegen/java/file_struct.go
@@ -166,6 +166,7 @@
 
 	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
 	data := struct {
+		FileDoc        string
 		AccessModifier string
 		Doc            string
 		Fields         []structDefinitionField
@@ -176,6 +177,7 @@
 		VdlTypeName    string
 		VdlTypeString  string
 	}{
+		FileDoc:        tdef.File.Package.FileDoc,
 		AccessModifier: accessModifierForName(tdef.Name),
 		Doc:            javaDocInComment(tdef.Doc),
 		Fields:         fields,
diff --git a/lib/vdl/codegen/java/file_union.go b/lib/vdl/codegen/java/file_union.go
index aca10b3..14b6d45 100644
--- a/lib/vdl/codegen/java/file_union.go
+++ b/lib/vdl/codegen/java/file_union.go
@@ -82,6 +82,7 @@
 	}
 	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
 	data := struct {
+		FileDoc        string
 		AccessModifier string
 		Doc            string
 		Fields         []unionDefinitionField
@@ -91,6 +92,7 @@
 		VdlTypeName    string
 		VdlTypeString  string
 	}{
+		FileDoc:        tdef.File.Package.FileDoc,
 		AccessModifier: accessModifierForName(tdef.Name),
 		Doc:            javaDocInComment(tdef.Doc),
 		Fields:         fields,
diff --git a/lib/vdl/codegen/javascript/gen.go b/lib/vdl/codegen/javascript/gen.go
index c213899..6403d4d 100644
--- a/lib/vdl/codegen/javascript/gen.go
+++ b/lib/vdl/codegen/javascript/gen.go
@@ -390,11 +390,7 @@
 // complicated logic is delegated to the helper functions above.
 //
 // We try to generate code that has somewhat reasonable formatting.
-const genJS = `// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-{{with $data := .}}
+const genJS = `{{with $data := .}}{{$data.Pkg.FileDoc}}
 // This file was auto-generated by the vanadium vdl tool.
 {{generateSystemImports $data}}
 
diff --git a/lib/vdl/compile/compile.go b/lib/vdl/compile/compile.go
index 2ce4696..feab45f 100644
--- a/lib/vdl/compile/compile.go
+++ b/lib/vdl/compile/compile.go
@@ -123,6 +123,9 @@
 	// Compile our various structures.  The order of these operations matters;
 	// e.g. we must compile types before consts, since consts may use a type
 	// defined in this package.
+	if compileFileDoc(pkg, pfiles, env); !env.Errors.IsEmpty() {
+		return nil
+	}
 	if compileImports(pkg, pfiles, env); !env.Errors.IsEmpty() {
 		return nil
 	}
@@ -141,6 +144,23 @@
 	return pkg
 }
 
+func compileFileDoc(pkg *Package, pfiles []*parse.File, env *Env) {
+	for index := range pfiles {
+		file, pfile := pkg.Files[index], pfiles[index]
+		if index == 0 {
+			pkg.FileDoc = pfile.Doc
+		} else if pkg.FileDoc != pfile.Doc {
+			// We force all file-doc to be the same, since *.vdl files aren't 1-to-1
+			// with the generated files in each language, e.g. Java creates one file
+			// per class, while Javascript creates a single file for the entire
+			// package.  For the common-case where we use file-doc for copyright
+			// headers, it also prevents the user from accidentally adding copyright
+			// headers to one file but not another, in the same package.
+			env.Errorf(file, parse.Pos{1, 1}, "all files in a package must have the same file doc (the comment on the first line of each *.vdl file that isn't package doc)")
+		}
+	}
+}
+
 func compileImports(pkg *Package, pfiles []*parse.File, env *Env) {
 	for index := range pfiles {
 		file, pfile := pkg.Files[index], pfiles[index]
diff --git a/lib/vdl/compile/result.go b/lib/vdl/compile/result.go
index b30437e..3de65f8 100644
--- a/lib/vdl/compile/result.go
+++ b/lib/vdl/compile/result.go
@@ -257,6 +257,10 @@
 	GenPath string
 	// Files holds the files contained in the package.
 	Files []*File
+	// FileDoc holds the top-level file documentation, which must be the same for
+	// every file in the package.  This is typically used to hold boilerplate that
+	// must appear in every generated file, e.g. a copyright notice.
+	FileDoc string
 	// Config holds the configuration for this package, specifying options used
 	// during compilation and code generation.
 	Config vdltool.Config
diff --git a/lib/vdl/parse/COPYRIGHT b/lib/vdl/parse/COPYRIGHT
deleted file mode 100644
index d3e8617..0000000
--- a/lib/vdl/parse/COPYRIGHT
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
diff --git a/lib/vdl/parse/grammar_gen.sh b/lib/vdl/parse/grammar_gen.sh
index a67ae64..dad7b5f 100755
--- a/lib/vdl/parse/grammar_gen.sh
+++ b/lib/vdl/parse/grammar_gen.sh
@@ -12,7 +12,12 @@
 
 go tool yacc -o grammar.y.tmp.go -v grammar.y.debug.tmp grammar.y
 gofmt -l -w grammar.y.tmp.go
-cat - COPYRIGHT grammar.y.tmp.go > grammar.y.go
+cat - grammar.y.tmp.go > grammar.y.go <<EOF
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+EOF
 cat - grammar.y.debug.tmp > grammar.y.debug <<EOF
 ***** PLEASE READ THIS! DO NOT DELETE THIS BLOCK! *****
 * The main reason this file has been generated and submitted is to try to ensure
diff --git a/lib/vdl/parse/parse.go b/lib/vdl/parse/parse.go
index aa38c43..ab74c83 100644
--- a/lib/vdl/parse/parse.go
+++ b/lib/vdl/parse/parse.go
@@ -65,6 +65,7 @@
 	}
 	config := &Config{
 		FileName:  fileName,
+		Doc:       file.Doc,
 		ConfigDef: file.PackageDef,
 		Imports:   file.Imports,
 		Config:    file.ConstDefs[0].Expr,
@@ -404,7 +405,7 @@
 
 // getDocSuffix returns the suffix documentation associated with pos.  Our rule
 // is the first line of the documentation must be on the same line as pos.  Once
-// a comment block as been returned it isn't eligible to be attached to any
+// a comment block has been returned it isn't eligible to be attached to any
 // other item, and is deleted from the map.
 //
 // The returned string is either empty, or has a leading space.
@@ -419,6 +420,24 @@
 	return doc
 }
 
+// getFileDoc returns the file documentation.  Our rule is that the first line
+// of the documentation must occur on the first line of the file, and all other
+// comments must have already been attached.  Once a comment block has been
+// returned it isn't eligible to be attached to any other item, and is deleted
+// from the map.
+//
+// The returned string is either empty, or is newline terminated.
+func (cm *commentMap) getFileDoc() string {
+	block := cm.byFirst[1]
+	if block.text == "" {
+		return ""
+	}
+	doc := block.text + "\n"
+	delete(cm.byFirst, block.firstLine)
+	delete(cm.byLast, block.lastLine)
+	return doc
+}
+
 func attachTypeComments(t Type, cm *commentMap, suffix bool) {
 	switch tu := t.(type) {
 	case *TypeEnum:
@@ -518,6 +537,8 @@
 			y.Doc = l.comments.getDoc(y.Pos)
 		}
 	}
+	// Finally attach the top-level file doc - this occurs on the first line.
+	f.Doc = l.comments.getFileDoc()
 }
 
 // nextToken uses the text/scanner package to scan the input for the next token.
diff --git a/lib/vdl/parse/parse_test.go b/lib/vdl/parse/parse_test.go
index 1e6dcfa..0b84e89 100644
--- a/lib/vdl/parse/parse_test.go
+++ b/lib/vdl/parse/parse_test.go
@@ -174,11 +174,19 @@
 `}},
 		nil},
 	{
-		"NotPackageDoc",
-		`// Extra newline, not package doc
+		"FileDocNoPackageDoc",
+		`// File doc, has extra newline so not package doc
 
 package testpkg`,
-		&parse.File{BaseName: "testfile", PackageDef: np("testpkg", 3, 9)},
+		&parse.File{BaseName: "testfile", PackageDef: np("testpkg", 3, 9), Doc: "// File doc, has extra newline so not package doc\n"},
+		nil},
+	{
+		"FileDocAndPackageDoc",
+		`// File doc
+
+// Package doc
+package testpkg`,
+		&parse.File{BaseName: "testfile", PackageDef: parse.NamePos{Name: "testpkg", Pos: pos(4, 9), Doc: "// Package doc\n"}, Doc: "// File doc\n"},
 		nil},
 	{
 		"FAILUnterminatedComment",
@@ -1050,14 +1058,25 @@
 			Config: cn("true", 4, 10)},
 		nil},
 	{
-		"NotConfigDoc",
-		`// Extra newline, not config doc
+		"FileDocNoConfigDoc",
+		`// File doc, has extra newline so not config doc
 
 config = true`,
 		&parse.Config{FileName: "testfile", ConfigDef: np("config", 3, 1),
+			Doc:    "// File doc, has extra newline so not config doc\n",
 			Config: cn("true", 3, 10)},
 		nil},
 	{
+		"FileDocAndConfigDoc",
+		`// File doc
+
+// Config doc
+config = true`,
+		&parse.Config{FileName: "testfile", ConfigDef: parse.NamePos{Name: "config", Pos: pos(4, 1), Doc: "// Config doc\n"},
+			Doc:    "// File doc\n",
+			Config: cn("true", 4, 10)},
+		nil},
+	{
 		"FAILUnterminatedComment",
 		`/* Unterminated
 Another line
diff --git a/lib/vdl/parse/result.go b/lib/vdl/parse/result.go
index 234154f..bdb71bb 100644
--- a/lib/vdl/parse/result.go
+++ b/lib/vdl/parse/result.go
@@ -59,8 +59,9 @@
 // File represents a parsed vdl file.
 type File struct {
 	BaseName   string       // Base name of the vdl file, e.g. "foo.vdl"
+	Doc        string       // Top-level file documentation
 	PackageDef NamePos      // Name, position and docs of the "package" clause
-	Imports    []*Import    // Imports listed in this file.
+	Imports    []*Import    // Imports listed in this file
 	ErrorDefs  []*ErrorDef  // Errors defined in this file
 	TypeDefs   []*TypeDef   // Types defined in this file
 	ConstDefs  []*ConstDef  // Consts defined in this file
@@ -71,6 +72,7 @@
 // vdl files, with similar concepts.
 type Config struct {
 	FileName  string      // Config file name, e.g. "a/b/foo.config"
+	Doc       string      // Top-level config file documentation
 	ConfigDef NamePos     // Name, position and docs of the "config" clause
 	Imports   []*Import   // Imports listed in this file.
 	Config    ConstExpr   // Const expression exported from this config.