Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main.zig
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const std = @import("std");
const Io = std.Io;

const rules = @import("rules/_list.zig");
pub const rules = @import("rules/_list.zig");

var io: Io = undefined;
var allocator: std.mem.Allocator = undefined;
pub var allocator: std.mem.Allocator = undefined;
Comment thread
IntegratedQuantum marked this conversation as resolved.

var failed: bool = false;

Expand Down
1 change: 1 addition & 0 deletions src/rules/_list.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ pub const imports = @import("imports.zig");
pub const tab_indentation = @import("tab_indentation.zig");
pub const trailing_space = @import("trailing_space.zig");
pub const orelse_unreachable = @import("orelse_unreachable.zig");
pub const unused_private_decls = @import("unused_private_decls.zig");
83 changes: 42 additions & 41 deletions src/rules/imports.zig
Original file line number Diff line number Diff line change
Expand Up @@ -29,53 +29,54 @@ fn isAliasAllowed(_importName: []const u8, _aliasName: []const u8) bool {
return std.mem.eql(u8, importName, aliasName);
}

pub fn isImport(ctx: main.Context, ast: std.zig.Ast, node: std.zig.Ast.Node.Index, mode: enum { errorOnAliasNameMismatch, ignoreAliasNameMismatch }) bool {
if (ast.nodeTag(node) != .simple_var_decl) return false;
const varDec = ast.simpleVarDecl(node);
const aliasName = ast.tokenSlice(varDec.ast.mut_token + 1);
const rhsNode = varDec.ast.init_node.unwrap() orelse return false;

switch (ast.nodeTag(rhsNode)) {
.builtin_call_two_comma => { // @import("x",)
const token = ast.nodeMainToken(rhsNode);
const importKeyword = ast.tokenSlice(token);
if (!std.mem.eql(u8, importKeyword, "@import")) return false;
ctx.printError("@import should not have a trailing comma.", ast.tokenStart(token));
return true;
},
.builtin_call_two => { // @import("x")
const importKeyword = ast.tokenSlice(ast.nodeMainToken(rhsNode));
if (!std.mem.eql(u8, importKeyword, "@import")) return false;

const token = ast.nodeMainToken(ast.nodeData(rhsNode).opt_node_and_opt_node[0].unwrap().?);
var importName = ast.tokenSlice(token);
importName = importName[1 .. importName.len - 1];

if (!isAliasAllowed(importName, aliasName)) {
ctx.printError("Encountered import with mismatched name", ast.tokenStart(token));
}
return true;
},
.field_access => { // alias
const token = ast.nodeData(rhsNode).node_and_token[1];
const importName = ast.tokenSlice(token);

if (!isAliasAllowed(importName, aliasName)) {
if (mode == .ignoreAliasNameMismatch) return false;
ctx.printError("Encountered alias with mismatched name", ast.tokenStart(token));
}
return true;
},
else => return false,
}
}

pub fn check(ctx: main.Context) void {
const ast = ctx.ast orelse return;
const root = ast.rootDecls();
var firstNonImportNode: ?std.zig.Ast.Node.Index = null;

for (root) |node| {
const isImport: bool = blk: {
if (ast.nodeTag(node) != .simple_var_decl) break :blk false;
const varDec = ast.simpleVarDecl(node);
const aliasName = ast.tokenSlice(varDec.ast.mut_token + 1);
const rhsNode = varDec.ast.init_node.unwrap().?;

switch (ast.nodeTag(rhsNode)) {
.builtin_call_two_comma => { // @import("x",)
const token = ast.nodeMainToken(rhsNode);
const importKeyword = ast.tokenSlice(token);
if (!std.mem.eql(u8, importKeyword, "@import")) break :blk false;
ctx.printError("@import should not have a trailing comma.", ast.tokenStart(token));
break :blk true;
},
.builtin_call_two => { // @import("x")
const importKeyword = ast.tokenSlice(ast.nodeMainToken(rhsNode));
if (!std.mem.eql(u8, importKeyword, "@import")) break :blk false;

const token = ast.nodeMainToken(ast.nodeData(rhsNode).opt_node_and_opt_node[0].unwrap().?);
var importName = ast.tokenSlice(token);
importName = importName[1 .. importName.len - 1];

if (!isAliasAllowed(importName, aliasName)) {
ctx.printError("Encountered import with mismatched name", ast.tokenStart(token));
}
break :blk true;
},
.field_access => { // alias
const token = ast.nodeData(rhsNode).node_and_token[1];
const importName = ast.tokenSlice(token);

if (!isAliasAllowed(importName, aliasName)) {
if (firstNonImportNode != null) break :blk false;
ctx.printError("Encountered alias with mismatched name", ast.tokenStart(token));
}
break :blk true;
},
else => break :blk false,
}
};
if (isImport) {
if (isImport(ctx, ast, node, if (firstNonImportNode != null) .ignoreAliasNameMismatch else .errorOnAliasNameMismatch)) {
if (firstNonImportNode) |nonImportNode| {
ctx.printError("Encountered import/alias after import section", ast.tokenStart(ast.firstToken(node)));
ctx.printInfo("determined end of import section", ast.tokenStart(ast.firstToken(nonImportNode)));
Expand Down
60 changes: 60 additions & 0 deletions src/rules/unused_private_decls.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const std = @import("std");

const main = @import("main");

pub fn check(ctx: main.Context) void {
const ast = ctx.ast orelse return;

var identifiers: std.StringHashMapUnmanaged(void) = .empty;
defer identifiers.deinit(main.allocator);

for (0..ast.nodes.len) |nodeIndex| {
const node: std.zig.Ast.Node.Index = @enumFromInt(nodeIndex);
switch (ast.nodeTag(node)) {
.identifier => {
_ = identifiers.getOrPut(main.allocator, ast.getNodeSource(node)) catch @panic("OOM");
},
.field_access => {
const token = ast.nodeData(node).node_and_token[1];
const name = ast.tokenSlice(token);
_ = identifiers.getOrPut(main.allocator, name) catch @panic("OOM");
},
.enum_literal => {
const name = ast.tokenSlice(ast.nodeMainToken(node));
_ = identifiers.getOrPut(main.allocator, name) catch @panic("OOM");
},
else => continue,
}
}

for (0..ast.nodes.len) |nodeIndex| {
const node: std.zig.Ast.Node.Index = @enumFromInt(nodeIndex);
switch (ast.nodeTag(node)) {
.simple_var_decl => {
if (main.rules.imports.isImport(ctx, ast, node, .ignoreAliasNameMismatch)) continue;
const varDec = ast.simpleVarDecl(node);
if (varDec.visib_token != null) continue;
const name = ast.tokenSlice(varDec.ast.mut_token + 1);
if (!identifiers.contains(name)) {
ctx.printError("Unused declaration", ast.tokenStart(varDec.firstToken()));
}
},
.fn_proto, .fn_proto_simple, .fn_proto_one, .fn_proto_multi => |tag| {
var buffer: [1]std.zig.Ast.Node.Index = undefined;
const functionProto = switch (tag) {
.fn_proto => ast.fnProto(node),
.fn_proto_simple => ast.fnProtoSimple(&buffer, node),
.fn_proto_one => ast.fnProtoOne(&buffer, node),
.fn_proto_multi => ast.fnProtoMulti(node),
else => unreachable,
};
if (functionProto.visib_token != null) continue;
const name = ast.tokenSlice(functionProto.name_token orelse continue);
if (!identifiers.contains(name)) {
ctx.printError("Unused declaration", ast.tokenStart(functionProto.firstToken()));
}
},
else => continue,
}
}
}
Loading