Skip to content

Instantly share code, notes, and snippets.

@vinipsmaker2
Last active August 9, 2018 16:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vinipsmaker2/d930fbe5b7597432b021effe618da171 to your computer and use it in GitHub Desktop.
Save vinipsmaker2/d930fbe5b7597432b021effe618da171 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# This script was tested against clang version 5.0.1
from __future__ import print_function
# CONFIG {{{
# Change this value if you want to use another user defined attribute to mark
# non-suspend blocks.
ATTRIBUTE = 'bt::forbid_suspend'
# }}}
import sys
import re
import clang.cindex
if len(sys.argv) < 2:
print('Usage:\n',
'\t', sys.argv[0], ' <source> [clang-args]\n',
'\n',
"You should specify at least '-std=c++11' in 'clang-args'",
sep='', file=sys.stderr)
sys.exit(-1)
last_extent = {
'start': (0, 0),
'end': (0, 0)
}
def get_my_extent(node):
global last_extent
if node.kind == clang.cindex.CursorKind.TRANSLATION_UNIT:
return {
'start': (0, 0),
'end': (0, 0)
}
elif node.location.file is None:
return last_extent
else:
return {
'start': (node.location.line - 1, node.location.column - 1),
'end': (node.extent.end.line - 1, node.extent.end.column - 1)
}
def location_cmp(a, b):
# compare lines first
if a[0] < b[0]:
return -1
elif a[0] > b[0]:
return 1
else:
# columns later
if a[1] < b[1]:
return -1
elif a[1] > b[1]:
return 1
else:
return 0
def gap_between_extents(first, second):
if location_cmp(first['end'], second['start']) > 0:
# `first` is a “grouping” node which encapsulates the latter node
return {
'start': first['start'],
'end': second['start']
}
else:
return {
'start': first['end'],
'end': second['start']
}
contents = open(sys.argv[1], 'r').readlines()
def has_forbid_suspend(extent):
global contents
interest = contents[extent['start'][0]:extent['end'][0]+1]
interest[-1] = interest[-1][:extent['end'][1]]
interest[0] = interest[0][extent['start'][1]:]
interest = ''.join(interest)
match = re.search('\\[\\[[^[]+]]', interest)
if match is None:
return False
return ATTRIBUTE in match.group()
def ast_has_yield(node):
if node.spelling == 'yield':
return True
else:
for c in node.get_children():
if ast_has_yield(c):
return True
return False
def walk(node, forbid_suspend=False):
global last_extent
if node.location.file is not None and node.location.file.name != sys.argv[1]:
return
node_extent = get_my_extent(node)
if (node.kind == clang.cindex.CursorKind.COMPOUND_STMT
and has_forbid_suspend(gap_between_extents(last_extent, node_extent))):
forbid_suspend = True
last_extent = node_extent
if node.kind == clang.cindex.CursorKind.CALL_EXPR:
args = [c for c in node.get_children()]
if forbid_suspend and len(args) > 0 and ast_has_yield(args[-1]):
print(sys.argv[1], ':', node.location.line, ':', node.location.column,
": error: call to possibly suspending '",
node.spelling,
"' coroutine within a '[[", ATTRIBUTE, "]]' block:\n",
contents[node.location.line-1],
' ' * (node.location.column - 1), '^',
sep='', file=sys.stderr)
sys.exit(-1)
for c in node.get_children():
walk(c, forbid_suspend)
index = clang.cindex.Index.create()
tu = index.parse(sys.argv[1], args=sys.argv[2:])
errors = [i for i in tu.diagnostics
if i.severity > clang.cindex.Diagnostic.Warning]
if len(errors) > 0:
for e in errors:
print(e.format())
sys.exit(-1)
walk(tu.cursor)
void g(int yield);
int t();
void f() {
int yield;
g(yield);
[[bt::forbid_suspend]] {
// Comment this call to make linter accept the code
g(yield);
t();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment