valavapicheck.vala 4.82 KB
Newer Older
1 2 3 4 5 6 7
/* valavapicheck.vala
 *
 * Copyright (C) 2007  Mathias Hasselmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 * 	Mathias Hasselmann <mathias.hasselmann@gmx.de>
 */

using GLib;

25
class Vala.VAPICheck {
26
	public VAPICheck (string gidlname, CodeContext context = new CodeContext ()) {
27 28
		gidl = new SourceFile (context, SourceFileType.SOURCE, gidlname);
		metadata = new SourceFile (context, SourceFileType.SOURCE, gidlname.substring (0, gidlname.length - 5) + ".metadata");
29
		this.context = context;
30 31
	}

32 33 34
	public CodeContext context { get; private set; }
	public SourceFile gidl { get; private set; }
	public SourceFile metadata { get; private set; }
35

36 37
	private List<string> _scope;
	private Set<string> _symbols;
38 39

	private void parse_gidl () {
40 41
		_scope = new ArrayList<string> ();
		_symbols = new HashSet<string> (str_hash, str_equal);
42

43 44 45 46 47 48 49
		try {
			foreach (weak IdlModule module in Idl.parse_file (gidl.filename)) {
				parse_members (module.name, module.entries);
			}
		} catch (MarkupError e) {
			stderr.printf ("%s: %s\n", gidl.filename, e.message);
                }
50 51
	}

52
	private void add_symbol (string name, string? separator = null) {
53 54 55 56 57 58 59 60 61

		if (null != separator) {
			string fullname = get_scope () + separator + name;
			_symbols.add (fullname);
		} else {
			_symbols.add (name);
		}
	}

62
	private string get_scope () {
63 64 65
		return _scope[_scope.size - 1];
	}

66
	private void enter_scope (string name) {
67 68 69 70 71 72 73 74
		_scope.add (name);
		add_symbol (name);
	}

	private void leave_scope () {
		_scope.remove_at (_scope.size - 1);
	}

75
	private void parse_members (string name, GLib.List<IdlNode> members) {
76 77 78 79 80 81 82 83 84 85 86
		enter_scope (name);

		foreach (weak IdlNode node in members) {
			switch (node.type) {
				case IdlNodeTypeId.ENUM:
					parse_members (((IdlNodeEnum) node).gtype_name,
								   ((IdlNodeEnum) node).values);
					break;

				case IdlNodeTypeId.FUNCTION:
					parse_members (((IdlNodeFunction) node).symbol,
87
								   (GLib.List<IdlNode>) ((IdlNodeFunction) node).parameters);
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
					break;

				case IdlNodeTypeId.BOXED:
					parse_members (((IdlNodeBoxed) node).gtype_name,
								   ((IdlNodeBoxed) node).members);
					break;

				case IdlNodeTypeId.INTERFACE:
				case IdlNodeTypeId.OBJECT:
					parse_members (((IdlNodeInterface) node).gtype_name,
								   ((IdlNodeInterface) node).members);
					break;

				case IdlNodeTypeId.FIELD:
				case IdlNodeTypeId.PARAM:
					add_symbol (node.name, ".");
					break;

				case IdlNodeTypeId.PROPERTY:
				case IdlNodeTypeId.SIGNAL:
					add_symbol (node.name, "::");
					break;

				case IdlNodeTypeId.STRUCT:
					parse_members (node.name, ((IdlNodeStruct) node).members);
					break;

				case IdlNodeTypeId.VALUE:
				case IdlNodeTypeId.VFUNC:
					// Not appliable?
					break;

				default:
					warning ("TODO: %s: Implement support for type %d nodes", node.name, node.type);
					break;
			}
		}

		leave_scope ();
	}

	private int check_metadata () {
		try {
			var metafile = new IOChannel.file (metadata.filename, "r");
132
			string line;
133 134 135 136 137 138
			int lineno = 1;

			while (IOStatus.NORMAL == metafile.read_line (out line, null, null)) {
				var tokens = line.split (" ", 2);
				var symbol = tokens[0];

139
				if (symbol.length > 0 && !_symbols.contains (symbol)) {
140
					var src = new SourceReference (metadata, SourceLocation (null, lineno, 1), SourceLocation (null, lineno, (int)symbol.length));
141 142 143 144 145 146 147
					Report.error (src, "Symbol `%s' not found".printf (symbol));
				}

				lineno += 1;
			}

			return 0;
148
		} catch (Error error) {
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
			Report.error (null, "%s: %s".printf (metadata.filename, error.message));
			return 1;
		}
	}

	public int run () {
		if (!FileUtils.test (gidl.filename, FileTest.IS_REGULAR)) {
			Report.error (null, "%s not found".printf (gidl.filename));
			return 2;
		}

		if (!FileUtils.test (metadata.filename, FileTest.IS_REGULAR)) {
			Report.error (null, "%s not found".printf (metadata.filename));
			return 2;
		}

		parse_gidl ();

		return check_metadata ();
	}

	static int main (string[] args) {
		if (2 != args.length || !args[1].has_suffix (".gidl")) {
			stdout.printf ("Usage: %s library.gidl\n",
				       Path.get_basename (args[0]));
			return 2;
		}

		var vapicheck = new VAPICheck (args[1]);
		return vapicheck.run ();
	}
}