summaryrefslogtreecommitdiffstats
path: root/Tools/c-analyzer/c_analyzer/variables/info.py
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2019-10-19 02:00:04 (GMT)
committerGitHub <noreply@github.com>2019-10-19 02:00:04 (GMT)
commite4c431ecf50def40eb93c3969c1e4eeaf7bf32f1 (patch)
tree071224bbded262901b9742eb82c5d82d2f744fe1 /Tools/c-analyzer/c_analyzer/variables/info.py
parentea55c51bd937f6019c35b39b87029644e469c059 (diff)
downloadcpython-e4c431ecf50def40eb93c3969c1e4eeaf7bf32f1.zip
cpython-e4c431ecf50def40eb93c3969c1e4eeaf7bf32f1.tar.gz
cpython-e4c431ecf50def40eb93c3969c1e4eeaf7bf32f1.tar.bz2
bpo-36876: Re-organize the c-analyzer tool code. (gh-16841)
This is partly a cleanup of the code. It also is preparation for getting the variables from the source (cross-platform) rather than from the symbols. The change only touches the tool (and its tests).
Diffstat (limited to 'Tools/c-analyzer/c_analyzer/variables/info.py')
-rw-r--r--Tools/c-analyzer/c_analyzer/variables/info.py93
1 files changed, 93 insertions, 0 deletions
diff --git a/Tools/c-analyzer/c_analyzer/variables/info.py b/Tools/c-analyzer/c_analyzer/variables/info.py
new file mode 100644
index 0000000..336a523
--- /dev/null
+++ b/Tools/c-analyzer/c_analyzer/variables/info.py
@@ -0,0 +1,93 @@
+from collections import namedtuple
+
+from ..common.info import ID, UNKNOWN
+from ..common.util import classonly, _NTBase
+
+
+def normalize_vartype(vartype):
+ """Return the canonical form for a variable type (or func signature)."""
+ # We allow empty strring through for semantic reasons.
+ if vartype is None:
+ return None
+
+ # XXX finish!
+ # XXX Return (modifiers, type, pointer)?
+ return str(vartype)
+
+
+# XXX Variable.vartype -> decl (Declaration).
+
+class Variable(_NTBase,
+ namedtuple('Variable', 'id storage vartype')):
+ """Information about a single variable declaration."""
+
+ __slots__ = ()
+
+ STORAGE = (
+ 'static',
+ 'extern',
+ 'implicit',
+ 'local',
+ )
+
+ @classonly
+ def from_parts(cls, filename, funcname, name, decl, storage=None):
+ varid = ID(filename, funcname, name)
+ if storage is None:
+ self = cls.from_id(varid, decl)
+ else:
+ self = cls(varid, storage, decl)
+ return self
+
+ @classonly
+ def from_id(cls, varid, decl):
+ from ..parser.declarations import extract_storage
+ storage = extract_storage(decl, infunc=varid.funcname)
+ return cls(varid, storage, decl)
+
+ def __new__(cls, id, storage, vartype):
+ self = super().__new__(
+ cls,
+ id=ID.from_raw(id),
+ storage=str(storage) if storage else None,
+ vartype=normalize_vartype(vartype) if vartype else None,
+ )
+ return self
+
+ def __hash__(self):
+ return hash(self.id)
+
+ def __getattr__(self, name):
+ return getattr(self.id, name)
+
+ def _validate_id(self):
+ if not self.id:
+ raise TypeError('missing id')
+
+ if not self.filename or self.filename == UNKNOWN:
+ raise TypeError(f'id missing filename ({self.id})')
+
+ if self.funcname and self.funcname == UNKNOWN:
+ raise TypeError(f'id missing funcname ({self.id})')
+
+ self.id.validate()
+
+ def validate(self):
+ """Fail if the object is invalid (i.e. init with bad data)."""
+ self._validate_id()
+
+ if self.storage is None or self.storage == UNKNOWN:
+ raise TypeError('missing storage')
+ elif self.storage not in self.STORAGE:
+ raise ValueError(f'unsupported storage {self.storage:r}')
+
+ if self.vartype is None or self.vartype == UNKNOWN:
+ raise TypeError('missing vartype')
+
+ @property
+ def isglobal(self):
+ return self.storage != 'local'
+
+ @property
+ def isconst(self):
+ return 'const' in self.vartype.split()